Beispiel #1
0
def default_ldap_user_password():
    # Manage primary LDAP user/password
    restricted_chars = ["'", "\"", "=", ";", ",", "$", "`", "~", "%", " "]
    password_regex = "^(?:(?=.*[a-z])(?:(?=.*[A-Z])(?=.*[\d\W])|(?=.*\W)(?=.*\d))|(?=.*\W)(?=.*[A-Z])(?=.*\d)).{8,}$"
    if args.ldap_password_file is None:
        install_parameters["ldap_password"] = get_input(f"[Step 4/{total_install_phases}] {install_phases[4]}", args.ldap_password, None, str, hide=True)
        while not re.match(password_regex, install_parameters["ldap_password"]) \
                or install_parameters["ldap_user"].lower() in install_parameters["ldap_password"].lower() \
                or [element for element in restricted_chars if (element in install_parameters["ldap_password"])]:
            print(f"{fg('red')} LDAP password must have 1 uppercase, 1 lowercase, 1 digit and be 8 chars min.\n LDAP password cannot contain your username, white space, or any of the following special characters {''.join(restricted_chars)}{attr('reset')}")
            install_parameters["ldap_password"] = get_input(f"[Step 4/{total_install_phases}] {install_phases[4]}", None, None, str, hide=True)

        if not args.ldap_password:
            # when pw is entered interactively via getpass, we ask for a verification
            ldap_password_verify = get_input("[Verification] Please re-enter the password of your first LDAP account", args.ldap_password, None, str, hide=True)
            if install_parameters["ldap_password"] != ldap_password_verify:
                print(f"{fg('red')} You entered two different passwords. Please try again and make sure password and password (verification) are the same.{attr('reset')}")
                return False
            else:
                return True
    else:
        # Retrieve password from file
        print(f"{fg('yellow')}Retrieving password from {args.ldap_password_file}{attr('reset')}")
        try:
            with open(args.ldap_password_file) as f:
                install_parameters["ldap_password"] = f.read().replace("\n", "").replace("\r", "")
                if not re.match(password_regex, install_parameters["ldap_password"]):
                    print(f"{fg('red')} LDAP password must have 1 uppercase, 1 lowercase, 1 digit and be 8 char min{attr('reset')}")
                    sys.exit(1)
        except FileNotFoundError:
            print(f"{fg('red')}Unable to found {args.ldap_password_file}. Please specify absolute path {attr('reset')}")
            sys.exit(1)
Beispiel #2
0
    def get_subnets(self, vpc_id, environment, selected_subnets=[]):
        try:
            if environment == "private":
                print(f"\n====== Select {fg('misty_rose_3')}3 subnets to use for your compute nodes (private subnets preferably) {attr('reset')} ======\n")
            else:
                print(f"\n====== Select {fg('misty_rose_3')}3 subnets to use for the main Scheduler and Load Balancer (public subnets preferably) {attr('reset')} ======\n")

            subnets_by_name = {}
            token = True
            next_token = None
            max_results = 50
            while token is True:
                if not next_token:
                    all_subnets = self.ec2.describe_subnets(MaxResults=max_results, Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}, {'Name': 'ipv6-native', 'Values': ['false']}])
                else:
                    all_subnets = self.ec2.describe_subnets(Filters=[{'Name': 'vpc-id', 'Values': [vpc_id]}, {'Name': 'ipv6-native', 'Values': ['false']}], MaxResults=max_results, NextToken=next_token)
                try:
                    next_token = all_subnets['Token']
                except KeyError:
                    token = False

                for subnet in all_subnets['Subnets']:
                    resource_name = False
                    if "Tags" in subnet.keys():
                        for tag in subnet["Tags"]:
                            if tag["Key"] == "Name":
                                resource_name = tag["Value"]
                    if not resource_name:
                        continue
                    subnets_by_name[resource_name] = subnet
            subnets = {}
            count = 1
            for resource_name in sorted(subnets_by_name):
                subnet = subnets_by_name[resource_name]
                if f"{subnet['SubnetId']},{subnet['AvailabilityZone']}" not in selected_subnets:
                    subnets[count] = {"id": subnet['SubnetId'],
                                        "availability_zone": subnet['AvailabilityZone'],
                                        "description": f"{resource_name if resource_name is not False else ''} {subnet['CidrBlock']} in {subnet['AvailabilityZone']}"}
                    count += 1

            [print("    {:2} > {}".format(key, value["description"])) for key, value in subnets.items()]

            selected_subnets_count = get_input(f"How many of these subnets do you want to use?", None, list(range(1, count)), int)
            while selected_subnets_count < 2:
                print(f"{fg('red')} You must use at least 2 subnets for high availability {attr('reset')}")
                selected_subnets_count = get_input(f"How many of these subnets do you want to use?", None, list(range(1, count)), int)

            selected_subnets = []
            while len(selected_subnets) != selected_subnets_count:
                allowed_choices = list(subnets.keys())
                if len(allowed_choices) == 0:
                    return {"success": False, "message": "Not enough subnets available"}
                choice = get_input(f"Choose your subnet #{len(selected_subnets) + 1} ?", None, allowed_choices, int)
                selected_subnets.append(f"{subnets[choice]['id']},{subnets[choice]['availability_zone']}")
                del subnets[choice]
            return {"success": True, "message": selected_subnets}

        except Exception as err:
            return {"success": False, "message": str(err)}
Beispiel #3
0
    def get_iam_roles(self, environment, selected_roles=[]):
        try:
            print(f"\n====== Choose the {fg('misty_rose_3')}IAM role to use for the {environment.upper()}{attr('reset')} ======\n")
            roles = {}
            count = 1
            marker = True
            next_marker = None
            max_items = 50
            while marker is True:
                if not next_marker:
                    all_roles = self.iam.list_roles(MaxItems=max_items)
                else:
                    all_roles = self.iam.list_roles(MaxItems=max_items, Marker=next_marker)
                try:
                    next_marker = all_roles['Marker']
                except KeyError:
                    marker = False

                for role in all_roles["Roles"]:
                    if role['RoleName'] not in selected_roles:
                        roles[count] = {"arn": f"{role['Arn']}", "name": role["RoleName"], "description": f"{role['RoleName']} - {role['Description'] if 'Description' in role.keys() else ''}"}
                        count += 1

            [print("    {} > {}".format(key, value["description"])) for key, value in roles.items()]
            allowed_choices = list(roles.keys())
            choice = get_input(f"What IAM Role for you want to use for {environment.upper()}", None, allowed_choices, int)
            return {'success': True, 'message': roles[choice]}

        except Exception as err:
            print(err)
            return {'success': False, 'message': str(err)}
Beispiel #4
0
    def find_directory_services(self, vpc_id):
        try:
            print(f"\n====== What {fg('misty_rose_3')}Directory Services (Microsoft AD){attr('reset')} do you want to use? [region: {self.region}, vpc: {vpc_id}] ======\n")
            ds = {}
            count = 1
            token = True
            next_token = None
            max_results = 50
            while token is True:
                if not next_token:
                    all_ds = self.ds.describe_directories(MaxResults=max_results)
                else:
                    all_ds = self.ds.describe_directories(MaxResults=max_results, NextToken=next_token)
                try:
                    next_token = all_ds['Token']
                except KeyError:
                    token = False

                for directory in all_ds["DirectoryDescriptions"]:
                    if directory["VpcSettings"]["VpcId"] == vpc_id:
                        ds[count] = {"id": directory["DirectoryId"],
                                     "name": directory["Name"],
                                     "netbios": directory["ShortName"],
                                     "dns": directory["DnsIpAddrs"],
                                     "description": f"{directory['Name']} (Domain: {directory['ShortName']}, Id: {directory['DirectoryId']})"}
                        count += 1
            [print("    {:2} > {}".format(key, value["description"])) for key, value in ds.items()]
            allowed_choices = list(ds.keys())
            choice = get_input(f"Choose the directory you want to use?", None, allowed_choices, int)
            return {"success": True, "message": ds[choice]}

        except Exception as err:
            return {"success": False, "message": str(err)}
Beispiel #5
0
    def find_vpc(self):
        try:
            print(f"\n====== What {fg('misty_rose_3')}VPC{attr('reset')} in {self.region} do you want to use? ======\n")
            vpcs_by_name = {}
            token = True
            next_token = None
            max_results = 50
            while token is True:
                if not next_token:
                    all_vpcs = self.ec2.describe_vpcs()
                else:
                    all_vpcs = self.ec2.describe_vpcs(MaxResults=max_results, NextToken=next_token)
                try:
                    next_token = all_vpcs['Token']
                except KeyError:
                    token = False

                for vpc in all_vpcs["Vpcs"]:
                    resource_name = False
                    if "Tags" in vpc.keys():
                        for tag in vpc["Tags"]:
                            if tag["Key"] == "Name":
                                resource_name = tag["Value"]
                        if not resource_name:
                            continue
                        vpcs_by_name[resource_name] = vpc
            vpcs = {}
            count = 1
            for resource_name in sorted(vpcs_by_name):
                vpc = vpcs_by_name[resource_name]
                vpcs[count] = {"id": vpc["VpcId"],
                                "description": f"{resource_name if resource_name is not False else ''} {vpc['VpcId']} {vpc['CidrBlock']}",
                                "cidr": vpc['CidrBlock']}
                count += 1
            [print("    {:2} > {}".format(key, value["description"])) for key, value in vpcs.items()]
            allowed_choices = list(vpcs.keys())
            choice = get_input(f"Choose the VPC you want to use?", None, allowed_choices, int)
            return {"success": True, "message": vpcs[choice]}

        except Exception as err:
            return {"success": False, "message": str(err)}
Beispiel #6
0
    def get_security_groups(self, vpc_id, environment, scheduler_sg=[]):
        try:
            print(f"\n====== Choose the {fg('misty_rose_3')}security group to use for the {environment.upper()}{attr('reset')} [region: {self.region}, vpc: {vpc_id}] ======\n")
            sgs_by_name = {}
            token = True
            next_token = None
            max_results = 50
            while token is True:
                if not next_token:
                    all_sgs = self.ec2.describe_security_groups(MaxResults=max_results, Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
                else:
                    all_sgs = self.ec2.describe_security_groups(MaxResults=max_results, NextToken=next_token, Filters=[{"Name": "vpc-id", "Values": [vpc_id]}])
                try:
                    next_token = all_sgs['Token']
                except KeyError:
                    token = False

                for sg in all_sgs['SecurityGroups']:
                    resource_name = False
                    if "Tags" in sg.keys():
                        for tag in sg["Tags"]:
                            if tag["Key"] == "Name":
                                resource_name = tag["Value"]
                    if not resource_name:
                        continue
                    sgs_by_name[resource_name] = sg
            sgs = {}
            count = 1
            for resource_name  in sorted(sgs_by_name):
                sg = sgs_by_name[resource_name]
                if sg['GroupId'] not in scheduler_sg:
                    sgs[count] = {"id": f"{sg['GroupId']}",
                                    "description": f"{resource_name if resource_name is not False else ''} {sg['GroupId']} {sg['GroupName']}"}
                    count += 1
            [print("    {:2} > {}".format(key, sgs[key]["description"])) for key in sorted(sgs)   ]
            allowed_choices = list(sgs.keys())
            choice = get_input(f"What security group for you want to use for {environment.upper()}", None, allowed_choices, int)
            return {'success': True, 'message': sgs[choice]["id"]}

        except Exception as err:
            return {'success': False, 'message': str(err)}
Beispiel #7
0
    def find_elasticsearch(self, vpc_id):
        try:
            print(f"\n====== What {fg('misty_rose_3')}ElasticSearch cluster{attr('reset')} do you want to use? [region: {self.region}, vpc: {vpc_id}] ======\n")
            es = {}
            count = 1
            # note: list_domain_names() does not seems to support pagination
            for es_cluster in self.es.list_domain_names()["DomainNames"]:
                es[count] = {"name": es_cluster["DomainName"]}
                count += 1
            [print("    {} > {}".format(key, value["name"])) for key, value in es.items()]
            allowed_choices = list(es.keys())
            choice = get_input(f"Choose the ElasticSearch Cluster you want to use?", None, allowed_choices, int)

            # note: describe_elasticsearch_domain() does not seems to support pagination
            domain_info = self.es.describe_elasticsearch_domain(DomainName=es[choice]["name"])
            if domain_info["DomainStatus"]["VPCOptions"]["VPCId"] == vpc_id:
                for scope, endpoint in domain_info["DomainStatus"]["Endpoints"].items():
                    es[choice]["endpoint"] = endpoint

            return {"success": True, "message": es[choice]}

        except Exception as err:
            return {"success": False, "message": str(err)}
Beispiel #8
0
    def validate_sg_rules(self, cfn_params, check_fs=True):
        try:
            # Begin Verify Security Group Rules
            print(f"\n====== Please wait a little as we {fg('misty_rose_3')}validate your security group rules {attr('reset')} ======\n")
            security_groups = [cfn_params["scheduler_sg"], cfn_params["compute_node_sg"]]
            if "vpc_endpoint_sg" in cfn_params:
                security_groups.append(cfn_params["vpc_endpoint_sg"])
            sg_rules = self.get_rules_for_security_group(security_groups)
            if check_fs is True:
                fs_sg = self.get_fs_security_groups(cfn_params)

            if sg_rules["success"] is True:
                scheduler_sg_rules = sg_rules["message"][cfn_params["scheduler_sg"]]
                compute_node_sg_rules = sg_rules["message"][cfn_params["compute_node_sg"]]
                vpc_endpoint_sg_rules = sg_rules["message"].get(cfn_params.get("vpc_endpoint_sg", None), None)
            else:
                print(f"{fg('red')}Error: {sg_rules['message']} {attr('reset')}")
                sys.exit(1)

            errors = {}
            # status == True means that the check passed
            errors["SCHEDULER_SG_IN_COMPUTE"] = {
                    "status": False,
                    "error": f"Compute Node SG must allow all TCP traffic from Scheduler SG",
                    "resolution": f"Add new rule on {cfn_params['compute_node_sg']} that allow TCP ports '0-65535' for {cfn_params['scheduler_sg']}"}
            errors["COMPUTE_SG_IN_SCHEDULER"] = {
                    "status": False,
                    "error": f"Scheduler SG must allow all TCP traffic from Compute Node SG",
                    "resolution": f"Add a new rule on {cfn_params['scheduler_sg']} that allow TCP ports '0-65535' for {cfn_params['compute_node_sg']}"}
            errors["CLIENT_IP_HTTPS_IN_SCHEDULER"] = {
                    "status": False,
                    "error": f"Client IP must be allowed for port 443 (80 optional) on Scheduler SG",
                    "resolution": f"Add two rules on {cfn_params['scheduler_sg']} that allow TCP ports 80 and 443 for {self.client_ip}"}
            errors["CLIENT_IP_SSH_IN_SCHEDULER"] = {
                    "status": False,
                    "error": f"Client IP must be allowed for port 22 (SSH) on Scheduler SG",
                    "resolution": f"Add one rule on {cfn_params['scheduler_sg']} that allow TCP port 22 for {self.client_ip}"}
            errors["SCHEDULER_SG_EQUAL_COMPUTE"] = {
                    "status": False,
                    "error": "Scheduler SG and Compute SG must be different",
                    "resolution": "You must choose two different security groups"}
            errors["COMPUTE_SG_EGRESS_EFA"] = {
                    "status": False,
                    "error": "Compute SG must reference egress traffic to itself for EFA",
                    "resolution": f"Add a new (EGRESS) rule on {cfn_params['compute_node_sg']} that allow TCP ports '0-65535' for {cfn_params['compute_node_sg']}. Make sure you configure EGRESS rule and not INGRESS"}
            if 'vpc_endpoint_sg' in cfn_params:
                errors["COMPUTE_EGRESS_TO_VPC_ENDPOINTS"] = {
                        "status": False,
                        "error": "Compute SG must allow port 443 egress to the vpc endpoints security group",
                        "resolution": f"Add a new (EGRESS) rule on {cfn_params['compute_node_sg']} that allows TCP port '443' for {cfn_params['vpc_endpoint_sg']}. Make sure you configure EGRESS rule and not INGRESS"}
                errors["VPC_ENDPOINTS_INGRESS_FROM_COMPUTE"] = {
                        "status": False,
                        "error": "vpc Endpoints SG must allow port 443 ingress from the Compute SG",
                        "resolution": f"Add a new (INGRESS) rule on {cfn_params['vpc_endpoint_sg']} that allows TCP port '443' from {cfn_params['compute_node_sg']}. Make sure you configure INGRESS rule and not EGRESS"}
                errors["SCHEDULER_EGRESS_TO_VPC_ENDPOINTS"] = {
                        "status": False,
                        "error": "Scheduler SG must allow port 443 egress to the vpc endpoints security group",
                        "resolution": f"Add a new (EGRESS) rule on {cfn_params['scheduler_sg']} that allows TCP port '443' for {cfn_params['vpc_endpoint_sg']}. Make sure you configure EGRESS rule and not INGRESS"}
                errors["VPC_ENDPOINTS_INGRESS_FROM_SCHEDULER"] = {
                        "status": False,
                        "error": "vpc Endpoints SG must allow port 443 ingress from the Scheduler SG",
                        "resolution": f"Add a new (INGRESS) rule on {cfn_params['vpc_endpoint_sg']} that allows TCP port '443' from {cfn_params['scheduler_sg']}. Make sure you configure INGRESS rule and not EGRESS"}

            if check_fs is True:
                errors["FS_APP_SG"] = {
                    "status": False,
                    "error": f"SG assigned to EFS App {cfn_params['fs_apps']} must allow Scheduler SG and Compute SG",
                    "resolution": f"Add {cfn_params['scheduler_sg']} and {cfn_params['compute_node_sg']} on your EFS Apps {cfn_params['fs_apps']}"}

                errors["FS_DATA_SG"] = {
                    "status": False,
                    "error": f"SG assigned to EFS App {cfn_params['fs_data']} must allow Scheduler SG and Compute SG",
                    "resolution": f"Add {cfn_params['scheduler_sg']} and {cfn_params['compute_node_sg']} on your EFS Data {cfn_params['fs_data']}"}

            # Verify Scheduler Rules
            for rules in scheduler_sg_rules:
                if rules["from_port"] == 0 and rules["to_port"] == 65535:
                    for rule in rules["approved_ips"]:
                        if cfn_params['compute_node_sg'] in rule:
                            errors["COMPUTE_SG_IN_SCHEDULER"]["status"] = True

                if rules["from_port"] == 443 or rules["from_port"] == 22:
                    for rule in rules["approved_ips"]:
                        client_ip_netmask = 32
                        if client_ip_netmask == '32':
                            if ipaddress.IPv4Address(self.client_ip) in ipaddress.IPv4Network(rule):
                                if rules["from_port"] == 443:
                                    errors["CLIENT_IP_HTTPS_IN_SCHEDULER"]["status"] = True
                                if rules["from_port"] == 22:
                                    errors["CLIENT_IP_SSH_IN_SCHEDULER"]["status"] = True
                        else:
                            if self.client_ip in rule:
                                if rules["from_port"] == 443:
                                    errors["CLIENT_IP_HTTPS_IN_SCHEDULER"]["status"] = True
                                if rules["from_port"] == 22:
                                    errors["CLIENT_IP_SSH_IN_SCHEDULER"]["status"] = True
            # Verify Compute Node Rules
            for rules in compute_node_sg_rules:
                if rules["from_port"] == 0 and rules["to_port"] == 65535:
                    for rule in rules["approved_ips"]:
                        if cfn_params['scheduler_sg'] in rule:
                            errors["SCHEDULER_SG_IN_COMPUTE"]["status"] = True

                        if rules["type"] == "egress":
                            if cfn_params['compute_node_sg'] in rule:
                                errors["COMPUTE_SG_EGRESS_EFA"]["status"] = True
            # Verify VPC Endpoint Rules
            if 'vpc_endpoint_sg' in cfn_params:
                for rule in compute_node_sg_rules:
                    # Make sure compute node allows egress to vpc endpoints
                    if rule["type"] != "egress":
                        continue
                    for approved_ip in rule["approved_ips"]:
                        if rule["from_port"] <= 443 and rule["to_port"] >= 443:
                            if cfn_params['vpc_endpoint_sg'] in approved_ip:
                                errors["COMPUTE_EGRESS_TO_VPC_ENDPOINTS"]["status"] = True
                for rule in scheduler_sg_rules:
                    # Make sure scheduler allows egress to vpc endpoints
                    if rule["type"] != "egress":
                        continue
                    for approved_ip in rule["approved_ips"]:
                        if rule["from_port"] <= 443 and rule["to_port"] >= 443:
                            if cfn_params['vpc_endpoint_sg'] in approved_ip:
                                errors["SCHEDULER_EGRESS_TO_VPC_ENDPOINTS"]["status"] = True
                for rule in vpc_endpoint_sg_rules:
                    # Make sure endpoints allow ingress from compute nodes and scheduler
                    if rule["type"] != "ingress":
                        continue
                    for approved_ip in rule["approved_ips"]:
                        if rule["from_port"] <= 443 and rule["to_port"] >= 443:
                            if cfn_params['scheduler_sg'] in approved_ip:
                                errors["VPC_ENDPOINTS_INGRESS_FROM_SCHEDULER"]["status"] = True
                            if cfn_params['compute_node_sg'] in approved_ip:
                                errors["VPC_ENDPOINTS_INGRESS_FROM_COMPUTE"]["status"] = True

            if check_fs is True:
                if cfn_params['scheduler_sg'] in fs_sg["message"][cfn_params['fs_apps']] and cfn_params['compute_node_sg'] in fs_sg["message"][cfn_params['fs_apps']]:
                    errors["FS_APP_SG"]["status"] = True

                if cfn_params['scheduler_sg'] in fs_sg["message"][cfn_params['fs_data']] and cfn_params['compute_node_sg'] in fs_sg["message"][cfn_params['fs_data']]:
                    errors["FS_DATA_SG"]["status"] = True

            if cfn_params["scheduler_sg"] != cfn_params["compute_node_sg"]:
                errors["SCHEDULER_SG_EQUAL_COMPUTE"]["status"] = True

            sg_errors = {}

            confirm_sg_settings = False
            for error_id, error_info in errors.items():
                if error_info["status"] is False:
                    if check_fs is False and "EFS" in error_id:
                        pass
                    else:
                        print(f"{fg('yellow')}ATTENTION!! {error_info['error']} {attr('reset')}\nHow to solve: {error_info['resolution']}\n")
                        sg_errors[error_info["error"]] = error_info["resolution"]
                        confirm_sg_settings = True

            if confirm_sg_settings:
                choice = get_input("Your security groups may not be configured correctly. Verify them and determine if the warnings listed above are false-positive.\n Do you still want to continue with the installation?",
                                   None, ["yes", "no"], str)
                if choice.lower() == "no":
                    sys.exit(1)
            else:
                print(f"{fg('green')} Security Groups seem to be configured correctly{attr('reset')}")

            return {"success": True,
                    "message": ""}

        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(f"{exc_type} {fname} {exc_tb.tb_lineno}")
            return {"success": False, "message": f"{exc_type} {fname} {exc_tb.tb_lineno}"}
Beispiel #9
0
    def get_fs(self, environment, vpc_id, selected_fs=[]):
        try:
            print(f"\n====== What {fg('misty_rose_3')}EFS/FSx for Lustre{attr('reset')} do you want to use for {fg('misty_rose_3')}{environment}{attr('reset')}? [region: {self.region}, vpc: {vpc_id}]  ======\n")
            filesystems = {}
            count = 1
            marker = True
            next_marker = None
            max_items = 50
            while marker is True:
                if not next_marker:
                    all_efs = self.efs.describe_file_systems(MaxItems=max_items)
                else:
                    all_efs = self.efs.describe_file_systems(MaxItems=max_items, Marker=next_marker)
                try:
                    next_marker = all_efs['Marker']
                except KeyError:
                    marker = False

                for filesystem in all_efs["FileSystems"]:
                    verified_vpc = False
                    for mount_target in self.efs.describe_mount_targets(FileSystemId=filesystem["FileSystemId"])["MountTargets"]:
                        if mount_target["VpcId"] == vpc_id:
                            verified_vpc = True

                    if verified_vpc is True:
                        if filesystem["FileSystemId"] not in selected_fs:
                            filesystems[count] = {"id": f"{filesystem['FileSystemId']}",
                                                  "description": f"EFS: {filesystem['Name'] if 'Name' in filesystem.keys() else 'EFS: '} {filesystem['FileSystemId']}.efs.{self.region}.amazonaws.com"}
                            count += 1

            efs_count = count - 1
            token = True
            next_token = None
            max_results = 50
            while token is True:
                if not next_token:
                    all_fsxl = self.fsx.describe_file_systems(MaxResults=max_results)
                else:
                    all_fsxl = self.fsx.describe_file_systems(MaxResults=max_results, NextToken=next_token)
                try:
                    next_token = all_fsxl['Token']
                except KeyError:
                    token = False

                for filesystem in all_fsxl["FileSystems"]:
                    resource_name = False
                    if filesystem["VpcId"] == vpc_id:
                        if filesystem["FileSystemId"] not in selected_fs:
                            for tag in filesystem['Tags']:
                                if tag["Key"] == 'Name':
                                    resource_name = tag["Value"]
                            filesystems[count] = {"id": f"{filesystem['FileSystemId']}",
                                                  "description": f"FSX for Lustre: {resource_name if resource_name is not False else 'FSx for Lustre: '} {filesystem['FileSystemId']}.fsx.{self.region}.amazonaws.com"}
                            count += 1

            [print("    {} > {}".format(key, value["description"])) for key, value in filesystems.items()]
            allowed_choices = list(filesystems.keys())
            choice = get_input(f"Choose the filesystem to use for {environment}?", None, allowed_choices, int)
            if choice <= efs_count:
                return {"success": True, "message": filesystems[choice]["id"], "provider": "efs"}
            else:
                return {"success": True, "message": filesystems[choice]["id"], "provider": "fsx_lustre"}

        except Exception as err:
            return {"success": False, "message": str(err)}
Beispiel #10
0
def get_install_parameters():
    # Retrieve User Specified Variables
    print("\n====== Validating SOCA Parameters ======\n")

    install_parameters["cluster_name"] = get_input(f"[Step 1/{total_install_phases}] {install_phases[1]}", args.name,None, str)
    while len(install_parameters["cluster_name"]) < 3 or len(install_parameters["cluster_name"]) > 11:
        print(f"{fg('red')}SOCA cluster name must greater than 3 chars and shorter than 11 characters (soca- is automatically added as a prefix) {attr('reset')}")
        install_parameters["cluster_name"] = get_input(f"[Step 1/{total_install_phases}] {install_phases[1]}", None, None, str)

    # Sanitize cluster name (remove any non alphanumerical character) or generate random cluster identifier
    sanitized_cluster_id = re.sub(r"\W+", "-", install_parameters["cluster_name"])
    sanitized_cluster_id = re.sub(r"soca-", "", sanitized_cluster_id)  # remove soca- if specified by the user
    install_parameters["cluster_id"] = f"soca-{sanitized_cluster_id.lower()}"  # do not remove soca- prefix or DCV IAM permission will not be working.

    install_parameters["bucket"] = get_input(f"[Step 2/{total_install_phases}] {install_phases[2]}", args.bucket, None,
                                             str)
    while check_bucket_permission(install_parameters["bucket"]) is False:
        install_parameters["bucket"] = get_input(f"[Step 2/{total_install_phases}] {install_phases[2]}", None, None, str)

    install_parameters["ldap_user"] = get_input(f"[Step 3/{total_install_phases}] {install_phases[3]}", args.ldap_user, None, str)
    while len(install_parameters["ldap_user"]) < 5 or not install_parameters["ldap_user"].isalnum():
        print(f"{fg('red')}LDAP user must be 5 characters mins and can only contains alphanumeric.{attr('reset')}")
        install_parameters["ldap_user"] = get_input(f"[Step 3/{total_install_phases}] {install_phases[3]}", None, None, str)

    if install_props.Config.directoryservice.provider == "activedirectory":
        while install_parameters["ldap_user"].lower() == "admin":
            print(f"{fg('yellow')} To prevent conflict with Directory Service, the first SOCA user cannot be named admin. Please pick a different name.{attr('reset')}")
            install_parameters["ldap_user"] = get_input(f"[Step 3/{total_install_phases}] {install_phases[3]}", None, None, str)

    create_ldap_user = default_ldap_user_password()
    while create_ldap_user is False:
        create_ldap_user = default_ldap_user_password()

    # Encode password to avoid any special char error while running bash CDK
    install_parameters["ldap_password"] = (base64.b64encode(install_parameters["ldap_password"].encode("utf-8"))).decode("utf-8")
    install_parameters["base_os"] = get_input(f"[Step 5/{total_install_phases}] {install_phases[5]}", args.base_os,["amazonlinux2", "centos7", "rhel7"], str)
    install_parameters["ssh_keypair"] = get_input(f"[Step 6/{total_install_phases}] {install_phases[6]}", args.ssh_keypair, accepted_aws_values["accepted_keypairs"], str)

    # Validate the prefix list id
    if args.prefix_list_id:
        try:
            found_prefix_list_id = ec2.describe_managed_prefix_lists(PrefixListIds=[args.prefix_list_id])['PrefixLists'][0]['PrefixListId']
            if found_prefix_list_id != args.prefix_list_id:
                raise RuntimeError(f"Found prefix list {found_prefix_list_id} does not match {args.prefix_list_id}. This is a programming error; please create an issue.")
            else:
                install_parameters["prefix_list_id"] = args.prefix_list_id
        except Exception as e:
            print(f"{fg('red')}Error. {args.prefix_list_id} not found. Check that it exists and starts with pl-.\nException:\n{e} {attr('reset')}")
            sys.exit(1)

    install_parameters["custom_ami"] = args.custom_ami if args.custom_ami else None

    # Network Configuration
    cidr_regex = r'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$'
    if not args.vpc_cidr:
        choice_vpc = get_input(f"[Step 7/{total_install_phases}] {install_phases[7]}", None, ["new", "existing"], str)
        if choice_vpc == "new":
            install_parameters["vpc_cidr"] = get_input("What CIDR do you want to use your your VPC? We recommend 10.0.0.0/16", args.vpc_cidr, None, str)
            while not re.match(cidr_regex, install_parameters["vpc_cidr"]):
                print(f"{fg('red')} Invalid CIDR {install_parameters['vpc_cidr']}. Format must be x.x.x.x/x (eg: 10.0.0.0/16){attr('reset')}")
                install_parameters["vpc_cidr"] = get_input("What CIDR do you want to use your your VPC? We recommend 10.0.0.0/16", None, None, str)
        else:
            # List all VPCs running on AWS account
            existing_vpc = FindExistingResource(install_parameters["region"], install_parameters["client_ip"]).find_vpc()
            if existing_vpc["success"] is True:
                install_parameters["vpc_id"] = existing_vpc["message"]["id"]
                install_parameters["vpc_cidr"] = existing_vpc["message"]["cidr"]
            else:
                sys.exit(1)

            # List all Subnets
            public_subnets = FindExistingResource(install_parameters["region"],
                                                  install_parameters["client_ip"]).get_subnets(install_parameters["vpc_id"], "public", [])
            if public_subnets["success"] is True:
                install_parameters["public_subnets"] = base64.b64encode(str(public_subnets["message"]).encode("utf-8")).decode("utf-8")
            else:
                print(f"{fg('red')}Error: {public_subnets['message']} {attr('reset')}")
                sys.exit(1)

            private_subnets = FindExistingResource(install_parameters["region"],
                                                   install_parameters["client_ip"]).get_subnets(install_parameters["vpc_id"], "private", public_subnets["message"])
            if private_subnets["success"] is True:
                install_parameters["private_subnets"] = base64.b64encode(str(private_subnets["message"]).encode("utf-8")).decode("utf-8")
            else:
                print(f"{fg('red')}Error: {private_subnets['message']} {attr('reset')}")
                sys.exit(1)

            vpc_azs = []
            for subnet in public_subnets["message"] + private_subnets["message"]:
                az = subnet.split(",")[1]
                if az not in vpc_azs:
                    vpc_azs.append(az)

                install_parameters["vpc_azs"] = ",".join(vpc_azs)
    else:
        install_parameters["vpc_cidr"] = args.vpc_cidr
        while not re.match(cidr_regex, install_parameters["vpc_cidr"]):
            print(f"{fg('red')} Invalid CIDR {install_parameters['vpc_cidr']}. Format must be x.x.x.x/x (eg: 10.0.0.0/16){attr('reset')}")
            install_parameters["vpc_cidr"] = get_input("What CIDR do you want to use your your VPC? We recommend 10.0.0.0/16", None, None, str)
    # Security Groups Configuration (only possible if user use an existing VPC)
    if install_parameters["vpc_id"]:
        choice_security_groups = get_input(f"[Step 8/{total_install_phases}] {install_phases[8]}", None, ["new", "existing"], str)
        if choice_security_groups == "existing":
            scheduler_sg = FindExistingResource(install_parameters["region"],
                                                install_parameters["client_ip"]).get_security_groups(install_parameters["vpc_id"], "scheduler", [])
            if scheduler_sg["success"] is True:
                install_parameters["scheduler_sg"] = scheduler_sg["message"]
            else:
                print(f"{fg('red')}Error: {scheduler_sg['message']} {attr('reset')}")
                sys.exit(1)

            compute_node_sg = FindExistingResource(install_parameters["region"],
                                                   install_parameters["client_ip"]).get_security_groups(install_parameters["vpc_id"], "compute nodes", install_parameters["scheduler_sg"])
            if compute_node_sg["success"] is True:
                install_parameters["compute_node_sg"] = compute_node_sg["message"]
            else:
                print(f"{fg('red')}Error: {compute_node_sg['message']} {attr('reset')}")
                sys.exit(1)

            if install_props.Config.network.vpc_interface_endpoints:
                vpc_endpoint_sg = FindExistingResource(install_parameters["region"],
                                                       install_parameters["client_ip"]).get_security_groups(install_parameters["vpc_id"], "vpc endpoints", install_parameters["scheduler_sg"])
                if vpc_endpoint_sg["success"] is True:
                    install_parameters["vpc_endpoint_sg"] = vpc_endpoint_sg["message"]
                else:
                    print(f"{fg('red')}Error: {vpc_endpoint_sg['message']} {attr('reset')}")
                    sys.exit(1)
            else:
                vpc_endpoint_sg = None

    # Filesystem Configuration (only possible if user use an existing VPC)
    if install_parameters["vpc_id"]:
        choice_filesystem = get_input(f"[Step 9/{total_install_phases}] {install_phases[9]}", None, ["new", "existing"],str)
        if choice_filesystem == "existing":
            # List FS
            fs_apps = FindExistingResource(install_parameters["region"], install_parameters["client_ip"]).get_fs("/apps", install_parameters["vpc_id"])
            if fs_apps["success"] is True:
                install_parameters["fs_apps_provider"] = fs_apps["provider"]
                install_parameters["fs_apps"] = fs_apps["message"]
            else:
                print(f"{fg('red')}Error: {fs_apps['message']} {attr('reset')}")
                sys.exit(1)

            fs_data = FindExistingResource(install_parameters["region"], install_parameters["client_ip"]).get_fs("/data", install_parameters["vpc_id"], selected_fs=install_parameters["fs_apps"])
            if fs_data["success"] is True:
                install_parameters["fs_data_provider"] = fs_data["provider"]
                install_parameters["fs_data"] = fs_data["message"]
                if install_parameters["fs_data"] == install_parameters["fs_apps"]:
                    print(f"{fg('red')}Error: EFS/FSx for Lustre /apps and /data must be different {attr('reset')}")
                    sys.exit(1)
            else:
                print(f"{fg('red')}Error: {fs_data['message']} {attr('reset')}")
                sys.exit(1)

    # Verify SG permissions
    if install_parameters["fs_apps"] or install_parameters["scheduler_sg"]:
        FindExistingResource(install_parameters["region"], install_parameters["client_ip"]).validate_sg_rules(install_parameters, check_fs=True if install_parameters["fs_apps"] else False)

    # AWS Directory Service Managed Active Directory configuration (only possible when using existing VPC)
    if install_props.Config.directoryservice.provider == "activedirectory":
        if install_parameters["vpc_id"]:
            choice_mad = get_input(f"[Step 10/{total_install_phases}] {install_phases[10]}", None, ["new", "existing"],str)
            if choice_mad == "existing":
                directory_service = FindExistingResource(install_parameters["region"],
                                                         install_parameters["client_ip"]).find_directory_services(install_parameters["vpc_id"])
                if directory_service["success"] is True:
                    install_parameters["directory_service_ds_user"] = get_input(f"Username of a domain user with  admin permissions?", None, None, str)
                    install_parameters["directory_service_ds_user_password"] = get_input(f"Password of the domain user with admin permissions", None, None, str)
                    install_parameters["directory_service"] = directory_service["message"]["id"]
                    install_parameters["directory_service_shortname"] = directory_service["message"]["netbios"]
                    install_parameters["directory_service_name"] = directory_service["message"]["name"]
                    install_parameters["directory_service_dns"] = directory_service["message"]["dns"]
                else:
                    print(f"{fg('red')}Error: {directory_service['message']} {attr('reset')}")
                    sys.exit(1)

    # ElasticSearch Configuration (only possible when using existing VPC)
    if install_parameters["vpc_id"]:
        choice_es = get_input(f"[Step 11/{total_install_phases}] {install_phases[11]}", None, ["new", "existing"], str)
        if choice_es == "existing":
            elasticsearch_cluster = FindExistingResource(install_parameters["region"],
                                                         install_parameters["client_ip"]).find_elasticsearch(install_parameters["vpc_id"])
            if elasticsearch_cluster["success"] is True:
                install_parameters["es_endpoint"] = elasticsearch_cluster["message"]["endpoint"]
            else:
                print(f"{fg('red')}Error: {elasticsearch_cluster['message']} {attr('reset')}")
                sys.exit(1)
    else:
        install_parameters["es_endpoint"] = None

    # IAM Roles configuration (only possible when using existing VPC)
    if install_parameters["vpc_id"]:
        choice_iam_roles = get_input(f"[Step 12/{total_install_phases}] {install_phases[12]}", None, ["new", "existing"], str)
        if choice_iam_roles == "existing":
            scheduler_role = FindExistingResource(install_parameters["region"],
                                                  install_parameters["client_ip"]).get_iam_roles("scheduler")
            if scheduler_role["success"] is True:
                install_parameters["scheduler_role_name"] = scheduler_role["message"]["name"]
                install_parameters["scheduler_role_arn"] = scheduler_role["message"]["arn"]
            else:
                print(f"{fg('red')}Error: {scheduler_role['message']} {attr('reset')}")
                sys.exit(1)

            if get_input(f"Was this role generated by a previous SOCA deployment? If yes, are you also using the same S3 bucket?", None, ["yes", "no"], str) == "yes":
                install_parameters["scheduler_role_from_previous_soca_deployment"] = True
            else:
                get_input(f"[IMPORTANT] Make sure this role is assumed by 'ec2.amazon.com' and 'ssm.amazonaws.com'\n Type ok to continue ...", None, ["ok"], str, color="yellow")

            compute_node_role = FindExistingResource(install_parameters["region"],
                                                     install_parameters["client_ip"]).get_iam_roles("compute nodes",
                                                                                                    selected_roles=[install_parameters["scheduler_role_name"]])
            if compute_node_role["success"] is True:
                install_parameters["compute_node_role_name"] = compute_node_role["message"]["name"]
                install_parameters["compute_node_role_arn"] = compute_node_role["message"]["arn"]
            else:
                print(f"{fg('red')}Error: {compute_node_role['message']} {attr('reset')}")
                sys.exit(1)

            if get_input(f"Was this role generated by a previous SOCA deployment?", None, ["yes", "no"], str) == "yes":
                install_parameters["compute_node_role_from_previous_soca_deployment"] = True
            else:
                get_input(f"[IMPORTANT] Make sure this role is assumed by 'ec2.amazon.com' and 'ssm.amazonaws.com'\n Type ok to continue ...", None, ["ok"], str, color="yellow")

            spotfleet_role = FindExistingResource(install_parameters["region"],
                                                  install_parameters["client_ip"]).get_iam_roles("spot fleet",
                                                                                                 selected_roles=[install_parameters["scheduler_role_name"], install_parameters["compute_node_role_name"]])
            if spotfleet_role["success"] is True:
                install_parameters["spotfleet_role_name"] = spotfleet_role["message"]["name"]
                install_parameters["spotfleet_role_arn"] = spotfleet_role["message"]["arn"]
            else:
                print(f"{fg('red')}Error: {spotfleet_role['message']} {attr('reset')}")
                sys.exit(1)

            if get_input(f"Was this role generated by a previous SOCA deployment?", None, ["yes", "no"], str) == "yes":
                install_parameters["spotfleet_role_from_previous_soca_deployment"] = True
            else:
                get_input(f"[IMPORTANT] Make sure this role is assumed by 'spotfleet.amazonaws.com'\n Type ok to continue ...",None, ["ok"], str, color="yellow")
Beispiel #11
0
        "compute_node_role_arn": None,
        "computenode_role_from_previous_soca_deployment": None,
        "scheduler_role_name": None,
        "scheduler_role_arn": None,
        "scheduler_role_from_previous_soca_deployment": None,
        "spotfleet_role_name": None,
        "spotfleet_role_arn": None,
        "spotfleet_role_from_previous_soca_deployment": None,
        # ElasticSearch
        "es_domain": None,
    }
    print("\n====== Validating Default SOCA Configuration ======\n")
    install_props = json.loads(json.dumps(get_install_properties(args.config)), object_hook=lambda d: SimpleNamespace(**d))

    if not args.skip_config_message:
        if get_input(f"SOCA will create AWS resources using the default parameters specified on installer/default_config.yml. \n Make sure you have read, reviewed and updated them (if needed). Enter 'yes' to continue ...", None, ["yes", "no"], str) != "yes":
            sys.exit(1)

    print("\n====== Validating AWS Environment ======\n")
    # Load AWS custom profile if specified
    if args.profile:
        try:
            session = boto3.session.Session(profile_name=args.profile)
        except ProfileNotFound:
            print(f"{fg('red')} Profile {args.profile} not found. Check ~/.aws/credentials file{attr('reset')}")
            sys.exit(1)
    else:
        session = boto3.session.Session()

    # Determine all AWS regions available on the account. We do not display opt-out region
    default_region = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")