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")
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)
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)
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")