in source/soca/cluster_web_ui/api/v1/dcv/create_windows_desktop.py [0: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["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)