コード例 #1
0
def create():
    parameters = {}
    for parameter in [
            "instance_type", "disk_size", "session_number", "session_name",
            "instance_ami", "hibernate", "subnet_id"
    ]:
        if not request.form[parameter]:
            parameters[parameter] = False
        else:
            if request.form[parameter].lower() in ["yes", "true"]:
                parameters[parameter] = True
            elif request.form[parameter].lower() in ["no", "false"]:
                parameters[parameter] = False
            else:
                parameters[parameter] = request.form[parameter]

    session_uuid = str(uuid.uuid4())
    region = os.environ["AWS_DEFAULT_REGION"]
    instance_type = parameters["instance_type"]
    soca_configuration = read_secretmanager.get_soca_configuration()
    instance_profile = soca_configuration["ComputeNodeInstanceProfileArn"]
    security_group_id = soca_configuration["ComputeNodeSecurityGroup"]
    if parameters["subnet_id"] is not False:
        soca_private_subnets = [parameters["subnet_id"]]
    else:
        soca_private_subnets = [
            soca_configuration["PrivateSubnet1"],
            soca_configuration["PrivateSubnet2"],
            soca_configuration["PrivateSubnet3"]
        ]

    # sanitize session_name, limit to 255 chars
    if parameters["session_name"] is False:
        session_name = 'WindowsDesktop' + str(parameters["session_number"])
    else:
        session_name = re.sub(r'\W+', '', parameters["session_name"])[:255]
        if session_name == "":
            # handle case when session name specified by user only contains invalid char
            session_name = 'WindowsDesktop' + str(parameters["session_number"])

    # Official DCV AMI
    # https://aws.amazon.com/marketplace/pp/B07TVL513S + https://aws.amazon.com/marketplace/pp/B082HYM34K
    # Non graphics is everything but g3/g4
    if parameters["instance_ami"] == "base":
        dcv_windows_ami = config.Config.DCV_WINDOWS_AMI
        if instance_type.startswith("g"):
            if region not in dcv_windows_ami["graphics"].keys(
            ) and parameters["instance_ami"] is False:
                flash(
                    "Sorry, Windows Desktop is not available on your AWS region. Base AMI are only available on {}"
                    .format(dcv_windows_ami["graphics"].keys()), "error")
                return redirect("/remote_desktop_windows")
            else:
                image_id = dcv_windows_ami["graphics"][region]
        else:
            if region not in dcv_windows_ami["non-graphics"].keys(
            ) and parameters["instance_ami"] is False:
                flash(
                    "Sorry, Windows Desktop is not available on your AWS region. Base AMI are only available on {}"
                    .format(dcv_windows_ami["non-graphics"].keys()), "error")

                return redirect("/remote_desktop_windows")
            else:
                image_id = dcv_windows_ami["non-graphics"][region]
    else:
        image_id = parameters["instance_ami"]
        if not image_id.startswith("ami-"):
            flash(
                "AMI selectioned {} does not seems to be valid. Must start with ami-<id>"
                .format(image_id), "error")
            return redirect("/remote_desktop_windows")

    digits = ([
        random.choice(''.join(random.choice(string.digits) for _ in range(10)))
        for _ in range(3)
    ])
    uppercase = ([
        random.choice(''.join(
            random.choice(string.ascii_uppercase) for _ in range(10)))
        for _ in range(3)
    ])
    lowercase = ([
        random.choice(''.join(
            random.choice(string.ascii_lowercase) for _ in range(10)))
        for _ in range(3)
    ])
    pw = digits + uppercase + lowercase
    session_local_admin_password = ''.join(random.sample(pw, len(pw)))
    user_data_script = open(
        "/apps/soca/" + soca_configuration["ClusterId"] +
        "/cluster_node_bootstrap/windows/ComputeNodeInstallDCVWindows.ps", "r")
    user_data = user_data_script.read()
    user_data_script.close()
    user_data = user_data.replace("%SOCA_LOCAL_ADMIN_PASSWORD%",
                                  session_local_admin_password)
    user_data = user_data.replace(
        "%SOCA_SchedulerPrivateIP%", soca_configuration['SchedulerPrivateIP'] +
        ":" + str(config.Config.FLASK_PORT))
    user_data = user_data.replace("%SOCA_LoadBalancerDNSName%",
                                  soca_configuration['LoadBalancerDNSName'])
    user_data = user_data.replace("%SOCA_LOCAL_USER%", session["user"])

    if config.Config.DCV_WINDOWS_AUTOLOGON is True:
        user_data = user_data.replace("%SOCA_WINDOWS_AUTOLOGON%", "true")
    else:
        user_data = user_data.replace("%SOCA_WINDOWS_AUTOLOGON%", "false")

    check_hibernation_support = client_ec2.describe_instance_types(
        InstanceTypes=[instance_type],
        Filters=[{
            "Name": "hibernation-supported",
            "Values": ["true"]
        }])
    logger.info("Checking in {} support Hibernation : {}".format(
        instance_type, check_hibernation_support))
    if len(check_hibernation_support["InstanceTypes"]) == 0:
        if config.Config.DCV_FORCE_INSTANCE_HIBERNATE_SUPPORT is True:
            flash(
                "Sorry your administrator limited <a href='https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/Hibernate.html#hibernating-prerequisites' target='_blank'>DCV to instances that support hibernation mode</a> <br> Please choose a different type of instance."
            )
            return redirect("/remote_desktop_windows")
        else:
            hibernate_support = False
    else:
        hibernate_support = True

    if parameters["hibernate"] and not hibernate_support:
        flash(
            "Sorry you have selected {} with hibernation support, but this instance type does not support it. Either disable hibernation support or pick a different instance type"
            .format(instance_type), "error")
        return redirect("/remote_desktop_windows")

    launch_parameters = {
        "security_group_id":
        security_group_id,
        "instance_profile":
        instance_profile,
        "instance_type":
        instance_type,
        "soca_private_subnets":
        soca_private_subnets,
        "user_data":
        user_data,
        "image_id":
        image_id,
        "session_name":
        session_name,
        "session_uuid":
        session_uuid,
        "base_os":
        "windows",
        "disk_size":
        parameters["disk_size"],
        "cluster_id":
        soca_configuration["ClusterId"],
        "hibernate":
        parameters["hibernate"],
        "user":
        session["user"],
        "DefaultMetricCollection":
        True
        if soca_configuration["DefaultMetricCollection"] == "true" else False,
        "SolutionMetricLambda":
        soca_configuration['SolutionMetricLambda'],
        "ComputeNodeInstanceProfileArn":
        soca_configuration["ComputeNodeInstanceProfileArn"]
    }
    dry_run_launch = can_launch_instance(launch_parameters)
    if dry_run_launch is True:
        launch_template = dcv_cloudformation_builder.main(**launch_parameters)
        if launch_template["success"] is True:
            cfn_stack_name = str(launch_parameters["cluster_id"] + "-" +
                                 launch_parameters["session_name"] + "-" +
                                 launch_parameters["user"])
            cfn_stack_tags = [{
                "Key": "soca:JobName",
                "Value": str(launch_parameters["session_name"])
            }, {
                "Key": "soca:JobOwner",
                "Value": str(session["user"])
            }, {
                "Key": "soca:JobProject",
                "Value": "desktop"
            }, {
                "Key": "soca:ClusterId",
                "Value": str(launch_parameters["cluster_id"])
            }, {
                "Key": "soca:NodeType",
                "Value": "dcv"
            }, {
                "Key": "soca:DCVSystem",
                "Value": "windows"
            }]
            try:
                client_cfn.create_stack(StackName=cfn_stack_name,
                                        TemplateBody=launch_template["output"],
                                        Tags=cfn_stack_tags)
            except Exception as e:
                logger.error(
                    f"Error while trying to provision {cfn_stack_name} due to {e}"
                )
                flash(
                    f"Error while trying to provision {cfn_stack_name} due to {e}"
                )
                return redirect("/remote_desktop_windows")
        else:
            flash(launch_template["output"], "error")
            return redirect("/remote_desktop_windows")
    else:
        flash(dry_run_launch, "error")
        return redirect("/remote_desktop_windows")

    flash(
        "Your session has been initiated. It will be ready within 10 minutes.",
        "success")
    new_session = WindowsDCVSessions(
        user=session["user"],
        session_number=parameters["session_number"],
        session_name=session_name,
        session_state="pending",
        session_host_private_dns=False,
        session_host_private_ip=False,
        session_instance_type=instance_type,
        dcv_authentication_token=None,
        session_local_admin_password=session_local_admin_password,
        session_id="console",
        tag_uuid=session_uuid,
        session_token=str(uuid.uuid4()),
        is_active=True,
        support_hibernation=parameters["hibernate"],
        created_on=datetime.utcnow(),
        schedule_monday_start=config.Config.DCV_WINDOWS_DEFAULT_SCHEDULE_START,
        schedule_tuesday_start=config.Config.
        DCV_WINDOWS_DEFAULT_SCHEDULE_START,
        schedule_wednesday_start=config.Config.
        DCV_WINDOWS_DEFAULT_SCHEDULE_START,
        schedule_thursday_start=config.Config.
        DCV_WINDOWS_DEFAULT_SCHEDULE_START,
        schedule_friday_start=config.Config.DCV_WINDOWS_DEFAULT_SCHEDULE_START,
        schedule_saturday_start=0,
        schedule_sunday_start=0,
        schedule_monday_stop=config.Config.DCV_WINDOWS_DEFAULT_SCHEDULE_STOP,
        schedule_tuesday_stop=config.Config.DCV_WINDOWS_DEFAULT_SCHEDULE_STOP,
        schedule_wednesday_stop=config.Config.
        DCV_WINDOWS_DEFAULT_SCHEDULE_STOP,
        schedule_thursday_stop=config.Config.DCV_WINDOWS_DEFAULT_SCHEDULE_STOP,
        schedule_friday_stop=config.Config.DCV_WINDOWS_DEFAULT_SCHEDULE_STOP,
        schedule_saturday_stop=0,
        schedule_sunday_stop=0,
    )
    db.session.add(new_session)
    db.session.commit()
    return redirect("/remote_desktop_windows")
コード例 #2
0
    def post(self, session_number):
        """
        Create a new DCV desktop session (Linux)
        ---
        tags:
          - DCV

        parameters:
          - in: body
            name: body
            schema:
              required:
                - instance_type
                - disk_size
                - session_number
                - instance_ami
                - subnet_id
                - hibernate
              properties:
                instance_type:
                  type: string
                  description: Type of EC2 instance to provision
                disk_size:
                  type: string
                  description: EBS size to provision for root device
                session_number:
                  type: string
                  description: DCV Session Number
                session_name:
                  type: string
                  description: DCV Session Name
                instance_ami:
                  type: string
                  description: Custom AMI to use
                subnet_id:
                  type: string
                  description: Specify a subnet id to launch the EC2
                hibernate:
                  type: string
                  description: True/False.
                user:
                  type: string
                  description: owner of the session
        responses:
          200:
            description: Pair of user/token is valid
          401:
            description: Invalid user/token pair
        """

        parser = reqparse.RequestParser()
        parser.add_argument("instance_type", type=str, location='form')
        parser.add_argument("disk_size", type=str, location='form')
        parser.add_argument("session_name", type=str, location='form')
        parser.add_argument("instance_ami", type=str, location='form')
        parser.add_argument("subnet_id", type=str, location='form')
        parser.add_argument("hibernate", type=str, location='form')
        args = parser.parse_args()
        logger.info(f"Received parameter for new Linux DCV session: {args}")

        if not args["subnet_id"]:
            args["subnet_id"] = False

        if session_number is None:
            return errors.all_errors(
                'CLIENT_MISSING_PARAMETER',
                "session_number not found in URL. Endpoint is /api/dcv/desktop/<session_number>/linux"
            )
        else:
            args["session_number"] = str(session_number)

        try:
            user = request.headers.get("X-SOCA-USER")
            if user is None:
                return errors.all_errors("X-SOCA-USER_MISSING")

            if not args["hibernate"]:
                args["hibernate"] = False
            elif args["hibernate"].lower() == "false":
                args["hibernate"] = False
            elif args["hibernate"].lower() == "true":
                args["hibernate"] = True
            else:
                return errors.all_errors(
                    "DCV_LAUNCH_ERROR",
                    f"hibernate must be either true or false")

            if args["instance_type"] is None:
                return errors.all_errors('CLIENT_MISSING_PARAMETER',
                                         "instance_type (str) is required.")

            args["disk_size"] = 30 if args["disk_size"] is None else args[
                "disk_size"]
            try:
                args["disk_size"] = int(args["disk_size"])
            except ValueError:
                return errors.all_errors("DCV_LAUNCH_ERROR",
                                         f"disk_size must be an integer")

            try:
                if int(args["session_number"]) > int(
                        config.Config.DCV_LINUX_SESSION_COUNT):
                    return errors.all_errors(
                        "DCV_LAUNCH_ERROR",
                        f"session_number {args['session_number']} is greater than the max number of session allowed ({config.Config.DCV_LINUX_SESSION_COUNT}). Contact admin for increase."
                    )
            except Exception as err:
                return errors.all_errors(
                    "DCV_LAUNCH_ERROR",
                    f"Session Number {args['session_number']} must be a number. Err: {err}"
                )

            session_uuid = str(uuid.uuid4())
            region = os.environ["AWS_DEFAULT_REGION"]
            instance_type = args["instance_type"]
            soca_configuration = read_secretmanager.get_soca_configuration()
            instance_profile = soca_configuration[
                "ComputeNodeInstanceProfileArn"]
            security_group_id = soca_configuration["ComputeNodeSecurityGroup"]

            if session_already_exist(args["session_number"]) is True:
                return errors.all_errors(
                    "DCV_LAUNCH_ERROR",
                    f"Session Number {args['session_number']} is already used by an active desktop. Terminate it first before being able to use the same number"
                )

            # sanitize session_name, limit to 255 chars
            if args["session_name"] is None:
                session_name = 'LinuxDesktop' + str(args["session_number"])
            else:
                session_name = re.sub(r'\W+', '', args["session_name"])[:255]
                if session_name == "":
                    # handle case when session name specified by user only contains invalid char
                    session_name = 'LinuxDesktop' + str(args["session_number"])

            if args["instance_ami"] is None or args["instance_ami"] == "base":
                image_id = soca_configuration["CustomAMI"]
                base_os = read_secretmanager.get_soca_configuration()['BaseOS']
            else:
                if len(args["instance_ami"].split(",")) != 2:
                    return errors.all_errors(
                        "DCV_LAUNCH_ERROR",
                        f"Invalid format for instance_ami,base_os : {args['instance_ami']}"
                    )

                image_id = args["instance_ami"].split(',')[0]
                base_os = args["instance_ami"].split(',')[1]
                if not image_id.startswith("ami-"):
                    return errors.all_errors(
                        "DCV_LAUNCH_ERROR",
                        f"AMI {image_id} does not seems to be valid. Must start with ami-<id>"
                    )
                else:
                    if validate_ec2_image(image_id) is False:
                        return errors.all_errors(
                            "DCV_LAUNCH_ERROR",
                            f"AMI {image_id} does not seems to be registered on SOCA. Refer to https://awslabs.github.io/scale-out-computing-on-aws/web-interface/create-virtual-desktops-images/"
                        )

            user_data = '''#!/bin/bash -x
            export PATH=$PATH:/usr/local/bin
            if [[ "''' + base_os + '''" == "centos7" ]] || [[ "''' + base_os + '''" == "rhel7" ]];
            then
                    yum install -y python3-pip
                    PIP=$(which pip3)
                    $PIP install awscli
                    yum install -y nfs-utils # enforce install of nfs-utils
            else
                 yum install -y python3-pip
                 PIP=$(which pip3)
                 $PIP install awscli
            fi
            if [[ "''' + base_os + '''" == "amazonlinux2" ]];
                then
                    /usr/sbin/update-motd --disable
            fi
    
            GET_INSTANCE_TYPE=$(curl http://169.254.169.254/latest/meta-data/instance-type)
            echo export "SOCA_DCV_AUTHENTICATOR="https://''' + soca_configuration[
                'SchedulerPrivateDnsName'] + ''':''' + config.Config.FLASK_PORT + '''/api/dcv/authenticator"" >> /etc/environment
            echo export "SOCA_DCV_SESSION_ID="''' + str(
                    session_uuid
                ) + '''"" >> /etc/environment
            echo export "SOCA_CONFIGURATION="''' + str(
                    soca_configuration['ClusterId']
                ) + '''"" >> /etc/environment
            echo export "SOCA_DCV_OWNER="''' + user + '''"" >> /etc/environment
            echo export "SOCA_BASE_OS="''' + str(
                    base_os
                ) + '''"" >> /etc/environment
            echo export "SOCA_JOB_TYPE="dcv"" >> /etc/environment
            echo export "SOCA_INSTALL_BUCKET="''' + str(
                    soca_configuration['S3Bucket']
                ) + '''"" >> /etc/environment
            echo export "SOCA_FSX_LUSTRE_BUCKET="false"" >> /etc/environment
            echo export "SOCA_FSX_LUSTRE_DNS="false"" >> /etc/environment
            echo export "SOCA_INSTALL_BUCKET_FOLDER="''' + str(
                    soca_configuration['S3InstallFolder']
                ) + '''"" >> /etc/environment
            echo export "SOCA_INSTANCE_TYPE=$GET_INSTANCE_TYPE" >> /etc/environment
            echo export "SOCA_HOST_SYSTEM_LOG="/apps/soca/''' + str(
                    soca_configuration['ClusterId']
                ) + '''/cluster_node_bootstrap/logs/desktop/''' + user + '''/''' + session_name + '''/$(hostname -s)"" >> /etc/environment
            echo export "AWS_DEFAULT_REGION="''' + region + '''"" >> /etc/environment
            echo export "SOCA_AUTH_PROVIDER="''' + str(
                    soca_configuration['AuthProvider']
                ).lower() + '''"" >> /etc/environment
            echo export "AWS_STACK_ID=${AWS::StackName}" >> /etc/environment
            echo export "AWS_DEFAULT_REGION=${AWS::Region}" >> /etc/environment
            # Required for proper EBS tagging
            echo export "SOCA_JOB_ID="''' + str(
                    session_name
                ) + '''"" >> /etc/environment
            echo export "SOCA_JOB_OWNER="''' + user + '''"" >> /etc/environment
            echo export "SOCA_JOB_PROJECT="dcv"" >> /etc/environment
            echo export "SOCA_JOB_QUEUE="dcv"" >> /etc/environment
    
    
            source /etc/environment
            AWS=$(which aws)
            # Give yum permission to the user on this specific machine
            echo "''' + user + ''' ALL=(ALL) /bin/yum" >> /etc/sudoers
            mkdir -p /apps
            mkdir -p /data

            FS_DATA_PROVIDER=''' + soca_configuration[
                    'FileSystemDataProvider'] + '''
            FS_DATA=''' + soca_configuration['FileSystemData'] + '''
            FS_APPS_PROVIDER=''' + soca_configuration[
                        'FileSystemAppsProvider'] + '''
            FS_APPS=''' + soca_configuration['FileSystemApps'] + '''

            if [[ "$FS_DATA_PROVIDER" == "fsx_lustre" ]] || [[ "$FS_APPS_PROVIDER" == "fsx_lustre" ]]; then
                if [[ -z "$(rpm -qa lustre-client)" ]]; then
                    # Install FSx for Lustre Client
                    if [[ "$SOCA_BASE_OS" == "amazonlinux2" ]]; then
                        amazon-linux-extras install -y lustre2.10
                    else
                        kernel=$(uname -r)
                        machine=$(uname -m)
                        echo "Found kernel version: $kernel running on: $machine"
                        yum -y install wget
                        if [[ $kernel == *"3.10.0-957"*$machine ]]; then
                            yum -y install https://downloads.whamcloud.com/public/lustre/lustre-2.10.8/el7/client/RPMS/x86_64/kmod-lustre-client-2.10.8-1.el7.x86_64.rpm
                            yum -y install https://downloads.whamcloud.com/public/lustre/lustre-2.10.8/el7/client/RPMS/x86_64/lustre-client-2.10.8-1.el7.x86_64.rpm
                        elif [[ $kernel == *"3.10.0-1062"*$machine ]]; then
                            wget https://fsx-lustre-client-repo-public-keys.s3.amazonaws.com/fsx-rpm-public-key.asc -O /tmp/fsx-rpm-public-key.asc
                            rpm --import /tmp/fsx-rpm-public-key.asc
                            wget https://fsx-lustre-client-repo.s3.amazonaws.com/el/7/fsx-lustre-client.repo -O /etc/yum.repos.d/aws-fsx.repo
                            sed -i 's#7#7.7#' /etc/yum.repos.d/aws-fsx.repo
                            yum clean all
                            yum install -y kmod-lustre-client lustre-client
                        elif [[ $kernel == *"3.10.0-1127"*$machine ]]; then
                            wget https://fsx-lustre-client-repo-public-keys.s3.amazonaws.com/fsx-rpm-public-key.asc -O /tmp/fsx-rpm-public-key.asc
                            rpm --import /tmp/fsx-rpm-public-key.asc
                            wget https://fsx-lustre-client-repo.s3.amazonaws.com/el/7/fsx-lustre-client.repo -O /etc/yum.repos.d/aws-fsx.repo
                            sed -i 's#7#7.8#' /etc/yum.repos.d/aws-fsx.repo
                            yum clean all
                            yum install -y kmod-lustre-client lustre-client
                        elif [[ $kernel == *"3.10.0-1160"*$machine ]]; then
                            wget https://fsx-lustre-client-repo-public-keys.s3.amazonaws.com/fsx-rpm-public-key.asc -O /tmp/fsx-rpm-public-key.asc
                            rpm --import /tmp/fsx-rpm-public-key.asc
                            wget https://fsx-lustre-client-repo.s3.amazonaws.com/el/7/fsx-lustre-client.repo -O /etc/yum.repos.d/aws-fsx.repo
                            yum clean all
                            yum install -y kmod-lustre-client lustre-client
                        elif [[ $kernel == *"4.18.0-193"*$machine ]]; then
                            # FSX for Lustre on aarch64 is supported only on 4.18.0-193
                            wget https://fsx-lustre-client-repo-public-keys.s3.amazonaws.com/fsx-rpm-public-key.asc -O /tmp/fsx-rpm-public-key.asc
                            rpm --import /tmp/fsx-rpm-public-key.asc
                            wget https://fsx-lustre-client-repo.s3.amazonaws.com/centos/7/fsx-lustre-client.repo -O /etc/yum.repos.d/aws-fsx.repo
                            yum clean all
                            yum install -y kmod-lustre-client lustre-client
                        else
                            echo "ERROR: Can't install FSx for Lustre client as kernel version: $kernel isn't matching expected versions: (x86_64: 3.10.0-957, -1062, -1127, -1160, aarch64: 4.18.0-193)!"
                        fi
                    fi
                fi
            fi

            if [[ "$FS_DATA_PROVIDER" == "efs" ]]; then
                echo "$FS_DATA:/ /data nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 0 0" >> /etc/fstab
            elif [[ "$FS_DATA_PROVIDER" == "fsx_lustre" ]]; then
                FSX_ID=$(echo $FS_DATA | cut -d. -f1)
                FSX_DATA_MOUNT_NAME=$($AWS fsx describe-file-systems --file-system-ids $FSX_ID  --query FileSystems[].LustreConfiguration.MountName --output text)
                echo "$FS_DATA@tcp:/$FSX_DATA_MOUNT_NAME /data lustre defaults,noatime,flock,_netdev 0 0" >> /etc/fstab
            fi

            if [[ "$FS_APPS_PROVIDER" == "efs" ]]; then
                echo "$FS_APPS:/ /apps nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 0 0" >> /etc/fstab
            elif [[ "$FS_APPS_PROVIDER" == "fsx_lustre" ]]; then
                FSX_ID=$(echo $FS_APPS | cut -d. -f1)
                FSX_APPS_MOUNT_NAME=$($AWS fsx describe-file-systems --file-system-ids $FSX_ID  --query FileSystems[].LustreConfiguration.MountName --output text)
                echo "$FS_APPS@tcp:/$FSX_APPS_MOUNT_NAME /apps lustre defaults,noatime,flock,_netdev 0 0" >> /etc/fstab
            fi

            FS_MOUNT=0
            mount -a 
            while [[ $? -ne 0 ]] && [[ $FS_MOUNT -lt 5 ]]
            do
                SLEEP_TIME=$(( RANDOM % 60 ))
                echo "Failed to mount FS, retrying in $SLEEP_TIME seconds and Loop $FS_MOUNT/5..."
                sleep $SLEEP_TIME
                ((FS_MOUNT++))
                mount -a
            done
                
            # Configure Chrony
            yum remove -y ntp
            yum install -y chrony
            mv /etc/chrony.conf  /etc/chrony.conf.original
            echo -e """
            # use the local instance NTP service, if available
            server 169.254.169.123 prefer iburst minpoll 4 maxpoll 4
    
            # Use public servers from the pool.ntp.org project.
            # Please consider joining the pool (http://www.pool.ntp.org/join.html).
            # !!! [BEGIN] SOCA REQUIREMENT
            # You will need to open UDP egress traffic on your security group if you want to enable public pool
            #pool 2.amazon.pool.ntp.org iburst
            # !!! [END] SOCA REQUIREMENT
            # Record the rate at which the system clock gains/losses time.
            driftfile /var/lib/chrony/drift
    
            # Allow the system clock to be stepped in the first three updates
            # if its offset is larger than 1 second.
            makestep 1.0 3
    
            # Specify file containing keys for NTP authentication.
            keyfile /etc/chrony.keys
    
            # Specify directory for log files.
            logdir /var/log/chrony
    
            # save data between restarts for fast re-load
            dumponexit
            dumpdir /var/run/chrony
            """ > /etc/chrony.conf
    
            systemctl enable chronyd
            # Prepare  Log folder
            mkdir -p $SOCA_HOST_SYSTEM_LOG
            echo "@reboot /bin/bash /apps/soca/$SOCA_CONFIGURATION/cluster_node_bootstrap/ComputeNodePostReboot.sh >> $SOCA_HOST_SYSTEM_LOG/ComputeNodePostReboot.log 2>&1" | crontab -
            cp /apps/soca/$SOCA_CONFIGURATION/cluster_node_bootstrap/config.cfg /root/
            /bin/bash /apps/soca/$SOCA_CONFIGURATION/cluster_node_bootstrap/ComputeNode.sh ''' + soca_configuration[
                            'SchedulerPrivateDnsName'] + ''' >> $SOCA_HOST_SYSTEM_LOG/ComputeNode.sh.log 2>&1'''

            if args["hibernate"]:
                try:
                    check_hibernation_support = client_ec2.describe_instance_types(
                        InstanceTypes=[instance_type],
                        Filters=[{
                            "Name": "hibernation-supported",
                            "Values": ["true"]
                        }])
                    logger.info(
                        "Checking in {} support Hibernation : {}".format(
                            instance_type, check_hibernation_support))
                    if len(check_hibernation_support["InstanceTypes"]) == 0:
                        if config.Config.DCV_FORCE_INSTANCE_HIBERNATE_SUPPORT is True:
                            return errors.all_errors(
                                "DCV_LAUNCH_ERROR",
                                f"Sorry your administrator limited <a href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html' target='_blank'>DCV to instances that support hibernation mode</a> <br> Please choose a different type of instance."
                            )
                        else:
                            return errors.all_errors(
                                "DCV_LAUNCH_ERROR",
                                f"Sorry you have selected {instance_type} with hibernation support, but this instance type does not support it. Either disable hibernation support or pick a different instance type"
                            )

                except ClientError as e:
                    return errors.all_errors(
                        "DCV_LAUNCH_ERROR",
                        f"Error while checking hibernation support due to {e}")

            launch_parameters = {
                "security_group_id":
                security_group_id,
                "instance_profile":
                instance_profile,
                "instance_type":
                instance_type,
                "soca_private_subnets":
                soca_configuration["PrivateSubnets"],
                "user_data":
                user_data,
                "subnet_id":
                args["subnet_id"],
                "image_id":
                image_id,
                "session_name":
                session_name,
                "session_uuid":
                session_uuid,
                "base_os":
                base_os,
                "disk_size":
                args["disk_size"],
                "cluster_id":
                soca_configuration["ClusterId"],
                "hibernate":
                args["hibernate"],
                "user":
                user,
                "DefaultMetricCollection":
                True if soca_configuration["DefaultMetricCollection"] == "true"
                else False,
                "SolutionMetricsLambda":
                soca_configuration['SolutionMetricsLambda'],
                "ComputeNodeInstanceProfileArn":
                soca_configuration["ComputeNodeInstanceProfileArn"]
            }
            dry_run_launch = can_launch_instance(launch_parameters)
            if dry_run_launch is True:
                launch_template = dcv_cloudformation_builder.main(
                    **launch_parameters)
                if launch_template["success"] is True:
                    cfn_stack_name = str(launch_parameters["cluster_id"] +
                                         "-" +
                                         launch_parameters["session_name"] +
                                         "-" + launch_parameters["user"])
                    cfn_stack_tags = [{
                        "Key":
                        "soca:JobName",
                        "Value":
                        str(launch_parameters["session_name"])
                    }, {
                        "Key": "soca:JobOwner",
                        "Value": user
                    }, {
                        "Key": "soca:JobProject",
                        "Value": "desktop"
                    }, {
                        "Key":
                        "soca:ClusterId",
                        "Value":
                        str(launch_parameters["cluster_id"])
                    }, {
                        "Key": "soca:NodeType",
                        "Value": "dcv"
                    }, {
                        "Key": "soca:DCVSystem",
                        "Value": base_os
                    }]
                    try:
                        client_cfn.create_stack(
                            StackName=cfn_stack_name,
                            TemplateBody=launch_template["output"],
                            Tags=cfn_stack_tags)
                    except Exception as e:
                        logger.error(
                            f"Error while trying to provision {cfn_stack_name} due to {e}"
                        )
                        return errors.all_errors(
                            "DCV_LAUNCH_ERROR",
                            f"Error while trying to provision {cfn_stack_name} due to {e}"
                        )
                else:
                    return errors.all_errors("DCV_LAUNCH_ERROR",
                                             f"{launch_template['output']}")
            else:
                return errors.all_errors("DCV_LAUNCH_ERROR",
                                         f" Dry Run error: {dry_run_launch}")

            new_session = LinuxDCVSessions(
                user=user,
                session_number=args["session_number"],
                session_name=session_name,
                session_state="pending",
                session_host_private_dns=False,
                session_host_private_ip=False,
                session_instance_type=instance_type,
                session_linux_distribution=base_os,
                dcv_authentication_token=None,
                session_id=session_uuid,
                tag_uuid=session_uuid,
                session_token=str(uuid.uuid4()),
                is_active=True,
                support_hibernation=args["hibernate"],
                created_on=datetime.utcnow(),
                schedule_monday_start=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_tuesday_start=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_wednesday_start=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_thursday_start=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_friday_start=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_saturday_start=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekend"]["start"],
                schedule_sunday_start=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekend"]["start"],
                schedule_monday_stop=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_tuesday_stop=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_wednesday_stop=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_thursday_stop=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_friday_stop=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_saturday_stop=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekend"]["stop"],
                schedule_sunday_stop=config.Config.
                DCV_LINUX_DEFAULT_SCHEDULE["weekend"]["stop"])
            db.session.add(new_session)
            db.session.commit()
            return {
                "success":
                True,
                "message":
                f"Session {session_name} with ID {args['session_number']} started successfully."
            }, 200
        except Exception as err:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            logger.error(exc_type, fname, exc_tb.tb_lineno)
            return errors.all_errors(type(err).__name__, err)
コード例 #3
0
    def post(self, session_number):
        """
        Create a new DCV desktop session (Windows)
        ---
        tags:
          - DCV

        parameters:
          - in: body
            name: body
            schema:
              required:
                - instance_type
                - disk_size
                - session_number
                - instance_ami
                - subnet_id
                - hibernate
              properties:
                instance_type:
                  type: string
                  description: Type of EC2 instance to provision
                disk_size:
                  type: string
                  description: EBS size to provision for root device
                session_number:
                  type: string
                  description: DCV Session Number
                session_name:
                  type: string
                  description: DCV Session Name
                instance_ami:
                  type: string
                  description: Custom AMI to use
                subnet_id:
                  type: string
                  description: Specify a subnet id to launch the EC2
                hibernate:
                  type: string
                  description: True/False.
                user:
                  type: string
                  description: owner of the session
        responses:
          200:
            description: Pair of user/token is valid
          401:
            description: Invalid user/token pair
        """

        parser = reqparse.RequestParser()
        parser.add_argument("instance_type", type=str, location='form')
        parser.add_argument("disk_size", type=str, location='form')
        parser.add_argument("session_name", type=str, location='form')
        parser.add_argument("instance_ami", type=str, location='form')
        parser.add_argument("subnet_id", type=str, location='form')
        parser.add_argument("hibernate", type=str, location='form')
        args = parser.parse_args()
        logger.info(f"Received parameter for new Windows DCV session: {args}")

        try:
            user = request.headers.get("X-SOCA-USER")
            if user is None:
                return errors.all_errors("X-SOCA-USER_MISSING")
            if not args["subnet_id"]:
                args["subnet_id"] = False
            if not args["hibernate"]:
                args["hibernate"] = False
            elif args["hibernate"].lower() == "false":
                args["hibernate"] = False
            elif args["hibernate"].lower() == "true":
                args["hibernate"] = True
            else:
                return errors.all_errors(
                    "DCV_LAUNCH_ERROR",
                    f"hibernate must be either true or false")

            if session_number is None:
                return errors.all_errors(
                    'CLIENT_MISSING_PARAMETER',
                    "session_number not found in URL. Endpoint is /api/dcv/desktop/<session_number>/windows"
                )
            else:
                args["session_number"] = str(session_number)

            if args["instance_type"] is None:
                return errors.all_errors('CLIENT_MISSING_PARAMETER',
                                         "instance_type (str) is required.")

            args["disk_size"] = 30 if args["disk_size"] is None else args[
                "disk_size"]
            try:
                args["disk_size"] = int(args["disk_size"])
            except ValueError:
                return errors.all_errors("DCV_LAUNCH_ERROR",
                                         f"disk_size must be an integer")

            try:
                if int(args["session_number"]) > int(
                        config.Config.DCV_WINDOWS_SESSION_COUNT):
                    return errors.all_errors(
                        "DCV_LAUNCH_ERROR",
                        f"session_number {args['session_number']} is greater than the max number of session allowed ({config.Config.DCV_WINDOWS_SESSION_COUNT}). Contact admin for increase."
                    )
            except Exception as err:
                return errors.all_errors(
                    "DCV_LAUNCH_ERROR",
                    f"Session Number {args['session_number']} must be a number. Err: {err}"
                )

            session_uuid = str(uuid.uuid4())
            region = os.environ["AWS_DEFAULT_REGION"]
            instance_type = args["instance_type"]
            soca_configuration = read_secretmanager.get_soca_configuration()
            instance_profile = soca_configuration[
                "ComputeNodeInstanceProfileArn"]
            security_group_id = soca_configuration["ComputeNodeSecurityGroup"]

            if session_already_exist(args["session_number"]) is True:
                return errors.all_errors(
                    "DCV_LAUNCH_ERROR",
                    f"Session Number {args['session_number']} is already used by an active desktop. Terminate it first before being able to use the same number"
                )

            # sanitize session_name, limit to 255 chars
            if args["session_name"] is None:
                session_name = 'WindowsDesktop' + str(args["session_number"])
            else:
                session_name = re.sub(r'\W+', '', args["session_name"])[:255]
                if session_name == "":
                    # handle case when session name specified by user only contains invalid char
                    session_name = 'WindowsDesktop' + str(
                        args["session_number"])

            # Official DCV AMI
            # https://aws.amazon.com/marketplace/pp/B07TVL513S + https://aws.amazon.com/marketplace/pp/B082HYM34K
            # Non graphics is everything but g3/g4
            if args["instance_ami"] is None or args["instance_ami"] == "base":
                dcv_windows_ami = config.Config.DCV_WINDOWS_AMI
                if instance_type.startswith("g"):
                    if instance_type.startswith("g4ad"):
                        if region not in dcv_windows_ami["graphics-amd"].keys(
                        ) and args["instance_ami"] is None:
                            return errors.all_errors(
                                "DCV_LAUNCH_ERROR",
                                f"Sorry, Windows Desktop is not available on your AWS region. Base AMI are only available on { dcv_windows_ami['graphics-amd'].keys()}"
                            )
                        else:
                            image_id = dcv_windows_ami["graphics-amd"][region]
                    else:
                        if region not in dcv_windows_ami["graphics"].keys(
                        ) and args["instance_ami"] is False:
                            return errors.all_errors(
                                "DCV_LAUNCH_ERROR",
                                f"Sorry, Windows Desktop is not available on your AWS region. Base AMI are only available on {dcv_windows_ami['graphics'].keys()}"
                            )
                        else:
                            image_id = dcv_windows_ami["graphics"][region]
                else:
                    if region not in dcv_windows_ami["non-graphics"].keys(
                    ) and args["instance_ami"] is False:
                        return errors.all_errors(
                            "DCV_LAUNCH_ERROR",
                            f"Sorry, Windows Desktop is not available on your AWS region. Base AMI are only available on {dcv_windows_ami['non-graphics'].keys()}"
                        )
                    else:
                        image_id = dcv_windows_ami["non-graphics"][region]
            else:
                if not args["instance_ami"].startswith("ami-"):
                    return errors.all_errors(
                        "DCV_LAUNCH_ERROR",
                        f"AMI {args['instance_ami']} does not seems to be valid. Must start with ami-<id>"
                    )
                else:
                    if validate_ec2_image(args["instance_ami"]) is False:
                        return errors.all_errors(
                            "DCV_LAUNCH_ERROR",
                            f"AMI {args['instance_ami']} does not seems to be registered on SOCA. Refer to https://awslabs.github.io/scale-out-computing-on-aws/web-interface/create-virtual-desktops-images/"
                        )
                    else:
                        image_id = args["instance_ami"]

            digits = ([
                random.choice(''.join(
                    random.choice(string.digits) for _ in range(10)))
                for _ in range(3)
            ])
            uppercase = ([
                random.choice(''.join(
                    random.choice(string.ascii_uppercase) for _ in range(10)))
                for _ in range(3)
            ])
            lowercase = ([
                random.choice(''.join(
                    random.choice(string.ascii_lowercase) for _ in range(10)))
                for _ in range(3)
            ])
            pw = digits + uppercase + lowercase
            session_local_admin_password = ''.join(random.sample(pw, len(pw)))
            user_data_script = open(
                "/apps/soca/" + soca_configuration["ClusterId"] +
                "/cluster_node_bootstrap/windows/ComputeNodeInstallDCVWindows.ps",
                "r")
            user_data = user_data_script.read()
            user_data_script.close()
            user_data = user_data.replace("%SOCA_LOCAL_ADMIN_PASSWORD%",
                                          session_local_admin_password)
            user_data = user_data.replace(
                "%SOCA_SchedulerPrivateIP%",
                soca_configuration['SchedulerPrivateIP'] + ":" +
                str(config.Config.FLASK_PORT))
            user_data = user_data.replace(
                "%SOCA_LoadBalancerDNSName%",
                soca_configuration['LoadBalancerDNSName'])
            user_data = user_data.replace("%SOCA_LOCAL_USER%", user)

            # required for EBS tagging
            user_data = user_data.replace("%SOCA_JOB_ID%", str(session_name))
            user_data = user_data.replace("%SOCA_JOB_OWNER%", user)
            user_data = user_data.replace("%SOCA_JOB_PROJECT%", "dcv")
            user_data = user_data.replace("%SOCA_AUTH_PROVIDER%",
                                          soca_configuration["AuthProvider"])
            user_data = user_data.replace(
                "%SOCA_DS_JOIN_USERNAME%",
                "false" if soca_configuration["AuthProvider"] == "openldap"
                else soca_configuration["DSDomainAdminUsername"])
            user_data = user_data.replace(
                "%SOCA_DS_JOIN_PASSWORD%",
                "false" if soca_configuration["AuthProvider"] == "openldap"
                else soca_configuration["DSDomainAdminPassword"])
            user_data = user_data.replace(
                "%SOCA_DS_NETBIOS%",
                "false" if soca_configuration["AuthProvider"] == "openldap"
                else soca_configuration["DSDomainNetbios"])
            user_data = user_data.replace(
                "%SOCA_DS_DOMAIN%",
                "false" if soca_configuration["AuthProvider"] == "openldap"
                else soca_configuration["DSDomainName"])

            if config.Config.DCV_WINDOWS_AUTOLOGON is True:
                user_data = user_data.replace("%SOCA_WINDOWS_AUTOLOGON%",
                                              "true")
            else:
                user_data = user_data.replace("%SOCA_WINDOWS_AUTOLOGON%",
                                              "false")

            if args["hibernate"]:
                try:
                    check_hibernation_support = client_ec2.describe_instance_types(
                        InstanceTypes=[instance_type],
                        Filters=[{
                            "Name": "hibernation-supported",
                            "Values": ["true"]
                        }])
                    logger.info(
                        "Checking in {} support Hibernation : {}".format(
                            instance_type, check_hibernation_support))
                    if len(check_hibernation_support["InstanceTypes"]) == 0:
                        if config.Config.DCV_FORCE_INSTANCE_HIBERNATE_SUPPORT is True:
                            return errors.all_errors(
                                "DCV_LAUNCH_ERROR",
                                f"Sorry your administrator limited <a href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html' target='_blank'>DCV to instances that support hibernation mode</a> <br> Please choose a different type of instance."
                            )
                        else:
                            return errors.all_errors(
                                "DCV_LAUNCH_ERROR",
                                f"Sorry you have selected {instance_type} with hibernation support, but this instance type does not support it. Either disable hibernation support or pick a different instance type"
                            )

                except ClientError as e:
                    return errors.all_errors(
                        "DCV_LAUNCH_ERROR",
                        f"Error while checking hibernation support due to {e}")

            launch_parameters = {
                "security_group_id":
                security_group_id,
                "instance_profile":
                instance_profile,
                "instance_type":
                instance_type,
                "soca_private_subnets":
                soca_configuration["PrivateSubnets"],
                "user_data":
                user_data,
                "subnet_id":
                args["subnet_id"],
                "image_id":
                image_id,
                "session_name":
                session_name,
                "session_uuid":
                session_uuid,
                "base_os":
                "windows",
                "disk_size":
                args["disk_size"],
                "cluster_id":
                soca_configuration["ClusterId"],
                "hibernate":
                args["hibernate"],
                "user":
                user,
                "DefaultMetricCollection":
                True if soca_configuration["DefaultMetricCollection"] == "true"
                else False,
                "SolutionMetricsLambda":
                soca_configuration['SolutionMetricsLambda'],
                "ComputeNodeInstanceProfileArn":
                soca_configuration["ComputeNodeInstanceProfileArn"]
            }
            dry_run_launch = can_launch_instance(launch_parameters)
            if dry_run_launch is True:
                launch_template = dcv_cloudformation_builder.main(
                    **launch_parameters)
                if launch_template["success"] is True:
                    cfn_stack_name = str(launch_parameters["cluster_id"] +
                                         "-" +
                                         launch_parameters["session_name"] +
                                         "-" + launch_parameters["user"])
                    cfn_stack_tags = [{
                        "Key":
                        "soca:JobName",
                        "Value":
                        str(launch_parameters["session_name"])
                    }, {
                        "Key": "soca:JobOwner",
                        "Value": user
                    }, {
                        "Key": "soca:JobProject",
                        "Value": "desktop"
                    }, {
                        "Key":
                        "soca:ClusterId",
                        "Value":
                        str(launch_parameters["cluster_id"])
                    }, {
                        "Key": "soca:NodeType",
                        "Value": "dcv"
                    }, {
                        "Key": "soca:DCVSystem",
                        "Value": "windows"
                    }]
                    try:
                        client_cfn.create_stack(
                            StackName=cfn_stack_name,
                            TemplateBody=launch_template["output"],
                            Tags=cfn_stack_tags)
                    except Exception as e:
                        logger.error(
                            f"Error while trying to provision {cfn_stack_name} due to {e}"
                        )
                        return errors.all_errors(
                            "DCV_LAUNCH_ERROR",
                            f"Error while trying to provision {cfn_stack_name} due to {e}"
                        )
                else:
                    return errors.all_errors("DCV_LAUNCH_ERROR",
                                             f"{launch_template['output']}")
            else:
                return errors.all_errors("DCV_LAUNCH_ERROR",
                                         f" Dry Run error: {dry_run_launch}")

            new_session = WindowsDCVSessions(
                user=user,
                session_number=args["session_number"],
                session_name=session_name,
                session_state="pending",
                session_host_private_dns=False,
                session_host_private_ip=False,
                session_instance_type=instance_type,
                dcv_authentication_token=None,
                session_local_admin_password=session_local_admin_password,
                session_id="console",
                tag_uuid=session_uuid,
                session_token=str(uuid.uuid4()),
                is_active=True,
                support_hibernation=args["hibernate"],
                created_on=datetime.utcnow(),
                schedule_monday_start=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_tuesday_start=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_wednesday_start=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_thursday_start=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_friday_start=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["start"],
                schedule_saturday_start=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekend"]["start"],
                schedule_sunday_start=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekend"]["start"],
                schedule_monday_stop=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_tuesday_stop=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_wednesday_stop=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_thursday_stop=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_friday_stop=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekdays"]["stop"],
                schedule_saturday_stop=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekend"]["stop"],
                schedule_sunday_stop=config.Config.
                DCV_WINDOWS_DEFAULT_SCHEDULE["weekend"]["stop"])
            db.session.add(new_session)
            db.session.commit()
            return {
                "success":
                True,
                "message":
                f"Session {session_name} with ID {args['session_number']} started successfully."
            }, 200
        except Exception as err:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            logger.error(exc_type, fname, exc_tb.tb_lineno)
            return errors.all_errors(type(err).__name__, err)
コード例 #4
0
def create():
    parameters = {}
    for parameter in [
            "instance_type", "disk_size", "session_number", "session_name",
            "instance_ami", "hibernate"
    ]:
        if not request.form[parameter]:
            parameters[parameter] = False
        else:
            if request.form[parameter].lower() in ["yes", "true"]:
                parameters[parameter] = True
            elif request.form[parameter].lower() in ["no", "false"]:
                parameters[parameter] = False
            else:
                parameters[parameter] = request.form[parameter]

    session_uuid = str(uuid.uuid4())
    region = os.environ["AWS_DEFAULT_REGION"]
    instance_type = parameters["instance_type"]
    soca_configuration = read_secretmanager.get_soca_configuration()
    instance_profile = soca_configuration["ComputeNodeInstanceProfileArn"]
    security_group_id = soca_configuration["ComputeNodeSecurityGroup"]
    soca_private_subnets = [
        soca_configuration["PrivateSubnet1"],
        soca_configuration["PrivateSubnet2"],
        soca_configuration["PrivateSubnet3"]
    ]

    # sanitize session_name, limit to 255 chars
    if parameters["session_name"] is False:
        session_name = 'LinuxDesktop' + str(parameters["session_number"])
    else:
        session_name = re.sub(r'\W+', '', parameters["session_name"])[:255]
        if session_name == "":
            # handle case when session name specified by user only contains invalid char
            session_name = 'LinuxDesktop' + str(parameters["session_number"])

    if parameters["instance_ami"] == "base":
        image_id = soca_configuration["CustomAMI"]
        base_os = read_secretmanager.get_soca_configuration()['BaseOS']
    else:
        if len(parameters["instance_ami"].split(",")) != 2:
            flash(
                "Invalid format for instance_ami,base_os : {}".format(
                    parameters["instance_ami"]), "error")
            return redirect("/remote_desktop")

        image_id = parameters["instance_ami"].split(',')[0]
        base_os = parameters["instance_ami"].split(',')[1]
        if not image_id.startswith("ami-"):
            flash(
                "AMI selectioned {} does not seems to be valid. Must start with ami-<id>"
                .format(image_id), "error")
            return redirect("/remote_desktop")

    user_data = '''#!/bin/bash -x
export PATH=$PATH:/usr/local/bin
if [[ "''' + base_os + '''" == "centos7" ]] || [[ "''' + base_os + '''" == "rhel7" ]];
then
        yum install -y python3-pip
        PIP=$(which pip3)
        $PIP install awscli
        yum install -y nfs-utils # enforce install of nfs-utils
else
     yum install -y python3-pip
     PIP=$(which pip3)
     $PIP install awscli
fi
if [[ "''' + base_os + '''" == "amazonlinux2" ]];
    then
        /usr/sbin/update-motd --disable
fi
    
GET_INSTANCE_TYPE=$(curl http://169.254.169.254/latest/meta-data/instance-type)
echo export "SOCA_DCV_AUTHENTICATOR="https://''' + soca_configuration[
        'SchedulerPrivateDnsName'] + ''':''' + config.Config.FLASK_PORT + '''/api/dcv/authenticator"" >> /etc/environment
echo export "SOCA_DCV_SESSION_ID="''' + str(
            session_uuid
        ) + '''"" >> /etc/environment
echo export "SOCA_CONFIGURATION="''' + str(
            soca_configuration['ClusterId']
        ) + '''"" >> /etc/environment
echo export "SOCA_DCV_OWNER="''' + str(
            session["user"]
        ) + '''"" >> /etc/environment
echo export "SOCA_BASE_OS="''' + str(base_os) + '''"" >> /etc/environment
echo export "SOCA_JOB_TYPE="dcv"" >> /etc/environment
echo export "SOCA_INSTALL_BUCKET="''' + str(
            soca_configuration['S3Bucket']
        ) + '''"" >> /etc/environment
echo export "SOCA_FSX_LUSTRE_BUCKET="false"" >> /etc/environment
echo export "SOCA_FSX_LUSTRE_DNS="false"" >> /etc/environment
echo export "SOCA_INSTALL_BUCKET_FOLDER="''' + str(
            soca_configuration['S3InstallFolder']
        ) + '''"" >> /etc/environment
echo export "SOCA_INSTANCE_TYPE=$GET_INSTANCE_TYPE" >> /etc/environment
echo export "SOCA_HOST_SYSTEM_LOG="/apps/soca/''' + str(
            soca_configuration['ClusterId']
        ) + '''/cluster_node_bootstrap/logs/desktop/''' + str(
            session["user"]
        ) + '''/''' + session_name + '''/$(hostname -s)"" >> /etc/environment
echo export "AWS_DEFAULT_REGION="''' + region + '''"" >> /etc/environment
# Required for proper EBS tagging
echo export "SOCA_JOB_ID="''' + str(session_name) + '''"" >> /etc/environment
echo export "SOCA_JOB_OWNER="''' + str(
            session["user"]
        ) + '''"" >> /etc/environment
echo export "SOCA_JOB_PROJECT="dcv"" >> /etc/environment
echo export "SOCA_JOB_QUEUE="dcv"" >> /etc/environment

source /etc/environment
AWS=$(which aws)
# Give yum permission to the user on this specific machine
echo "''' + session['user'] + ''' ALL=(ALL) /bin/yum" >> /etc/sudoers
mkdir -p /apps
mkdir -p /data
# Mount EFS
echo "''' + soca_configuration[
            'EFSDataDns'] + ''':/ /data nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 0 0" >> /etc/fstab
echo "''' + soca_configuration[
                'EFSAppsDns'] + ''':/ /apps nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport 0 0" >> /etc/fstab
EFS_MOUNT=0
mount -a 
while [[ $? -ne 0 ]] && [[ $EFS_MOUNT -lt 5 ]]
    do
    SLEEP_TIME=$(( RANDOM % 60 ))
    echo "Failed to mount EFS, retrying in $SLEEP_TIME seconds and Loop $EFS_MOUNT/5..."
    sleep $SLEEP_TIME
    ((EFS_MOUNT++))
    mount -a
  done

# Configure Chrony
yum remove -y ntp
yum install -y chrony
mv /etc/chrony.conf  /etc/chrony.conf.original
echo -e """
# use the local instance NTP service, if available
server 169.254.169.123 prefer iburst minpoll 4 maxpoll 4

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
# !!! [BEGIN] SOCA REQUIREMENT
# You will need to open UDP egress traffic on your security group if you want to enable public pool
#pool 2.amazon.pool.ntp.org iburst
# !!! [END] SOCA REQUIREMENT
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift

# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3

# Specify file containing keys for NTP authentication.
keyfile /etc/chrony.keys

# Specify directory for log files.
logdir /var/log/chrony

# save data between restarts for fast re-load
dumponexit
dumpdir /var/run/chrony
""" > /etc/chrony.conf

systemctl enable chronyd
# Prepare  Log folder
mkdir -p $SOCA_HOST_SYSTEM_LOG
echo "@reboot /bin/bash /apps/soca/$SOCA_CONFIGURATION/cluster_node_bootstrap/ComputeNodePostReboot.sh >> $SOCA_HOST_SYSTEM_LOG/ComputeNodePostReboot.log 2>&1" | crontab -
$AWS s3 cp s3://$SOCA_INSTALL_BUCKET/$SOCA_INSTALL_BUCKET_FOLDER/scripts/config.cfg /root/
/bin/bash /apps/soca/$SOCA_CONFIGURATION/cluster_node_bootstrap/ComputeNode.sh ''' + soca_configuration[
                    'SchedulerPrivateDnsName'] + ''' >> $SOCA_HOST_SYSTEM_LOG/ComputeNode.sh.log 2>&1'''

    check_hibernation_support = client_ec2.describe_instance_types(
        InstanceTypes=[instance_type],
        Filters=[{
            "Name": "hibernation-supported",
            "Values": ["true"]
        }])
    logger.info("Checking in {} support Hibernation : {}".format(
        instance_type, check_hibernation_support))
    if len(check_hibernation_support["InstanceTypes"]) == 0:
        if config.Config.DCV_FORCE_INSTANCE_HIBERNATE_SUPPORT is True:
            flash(
                "Sorry your administrator limited <a href='https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html' target='_blank'>DCV to instances that support hibernation mode</a> <br> Please choose a different type of instance."
            )
            return redirect("/remote_desktop")
        else:
            hibernate_support = False
    else:
        hibernate_support = True

    if parameters["hibernate"] and not hibernate_support:
        flash(
            "Sorry you have selected {} with hibernation support, but this instance type does not support it. Either disable hibernation support or pick a different instance type"
            .format(instance_type), "error")
        return redirect("/remote_desktop")

    launch_parameters = {
        "security_group_id":
        security_group_id,
        "instance_profile":
        instance_profile,
        "instance_type":
        instance_type,
        "soca_private_subnets":
        soca_private_subnets,
        "user_data":
        user_data,
        "image_id":
        image_id,
        "session_name":
        session_name,
        "session_uuid":
        session_uuid,
        "base_os":
        base_os,
        "disk_size":
        parameters["disk_size"],
        "cluster_id":
        soca_configuration["ClusterId"],
        "hibernate":
        parameters["hibernate"],
        "user":
        session["user"],
        "DefaultMetricCollection":
        True
        if soca_configuration["DefaultMetricCollection"] == "true" else False,
        "SolutionMetricLambda":
        soca_configuration['SolutionMetricLambda'],
        "ComputeNodeInstanceProfileArn":
        soca_configuration["ComputeNodeInstanceProfileArn"]
    }
    dry_run_launch = can_launch_instance(launch_parameters)
    if dry_run_launch is True:
        launch_template = dcv_cloudformation_builder.main(**launch_parameters)
        if launch_template["success"] is True:
            cfn_stack_name = str(launch_parameters["cluster_id"] + "-" +
                                 launch_parameters["session_name"] + "-" +
                                 launch_parameters["user"])
            cfn_stack_tags = [{
                "Key": "soca:JobName",
                "Value": str(launch_parameters["session_name"])
            }, {
                "Key": "soca:JobOwner",
                "Value": str(session["user"])
            }, {
                "Key": "soca:JobProject",
                "Value": "desktop"
            }, {
                "Key": "soca:ClusterId",
                "Value": str(launch_parameters["cluster_id"])
            }, {
                "Key": "soca:NodeType",
                "Value": "dcv"
            }, {
                "Key": "soca:DCVSystem",
                "Value": base_os
            }]
            try:
                client_cfn.create_stack(StackName=cfn_stack_name,
                                        TemplateBody=launch_template["output"],
                                        Tags=cfn_stack_tags)
            except Exception as e:
                logger.error(
                    f"Error while trying to provision {cfn_stack_name} due to {e}"
                )
                flash(
                    f"Error while trying to provision {cfn_stack_name} due to {e}"
                )
                return redirect("/remote_desktop")
        else:
            flash(launch_template["output"], "error")
            return redirect("/remote_desktop")
    else:
        flash(dry_run_launch, "error")
        return redirect("/remote_desktop")

    flash(
        "Your session has been initiated. It will be ready within 10 minutes.",
        "success")
    new_session = LinuxDCVSessions(
        user=session["user"],
        session_number=parameters["session_number"],
        session_name=session_name,
        session_state="pending",
        session_host_private_dns=False,
        session_host_private_ip=False,
        session_instance_type=instance_type,
        session_linux_distribution=base_os,
        dcv_authentication_token=None,
        session_id=session_uuid,
        tag_uuid=session_uuid,
        session_token=str(uuid.uuid4()),
        is_active=True,
        support_hibernation=parameters["hibernate"],
        created_on=datetime.utcnow(),
        schedule_monday_start=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_START,
        schedule_tuesday_start=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_START,
        schedule_wednesday_start=config.Config.
        DCV_LINUX_DEFAULT_SCHEDULE_START,
        schedule_thursday_start=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_START,
        schedule_friday_start=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_START,
        schedule_saturday_start=0,
        schedule_sunday_start=0,
        schedule_monday_stop=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_STOP,
        schedule_tuesday_stop=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_STOP,
        schedule_wednesday_stop=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_STOP,
        schedule_thursday_stop=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_STOP,
        schedule_friday_stop=config.Config.DCV_LINUX_DEFAULT_SCHEDULE_STOP,
        schedule_saturday_stop=0,
        schedule_sunday_stop=0,
    )
    db.session.add(new_session)
    db.session.commit()
    return redirect("/remote_desktop")