def __init__()

in source/idea/idea-cluster-manager/src/ideaclustermanager/app/accounts/helpers/preset_computer_helper.py [0:0]


    def __init__(self, context: SocaContext, ad_automation_dao: ADAutomationDAO, sender_id: str, request: Dict):
        """
        :param context:
        :param ad_automation_dao:
        :param sender_id: SenderId attribute from SQS Message
        :param request: the original request payload envelope
        """
        self.context = context
        self.ldap_client = ActiveDirectoryClient(self.context.logger())
        self.service_account_username, self.service_account_password = (
            self.ldap_client.fetch_service_account_credentials()
        )
        self.ad_automation_dao = ad_automation_dao
        self.sender_id = sender_id
        self.request = request

        self.logger = context.logger('preset-computer')

        self.nonce: Optional[str] = None
        self.instance_id: Optional[str] = None
        self.ec2_instance: Optional[EC2Instance] = None
        self.hostname: Optional[str] = None

        # Metadata for AD joins
        self.aws_account = self.context.config().get_string('cluster.aws.account_id', required=True)
        self.cluster_name = self.context.config().get_string('cluster.cluster_name', required=True)
        self.aws_region = self.context.config().get_string('cluster.aws.region', required=True)
        # parse and validate sender_id and request
        payload = Utils.get_value_as_dict('payload', request, {})

        # SenderId attribute from SQS message to protect against spoofing.
        if Utils.is_empty(sender_id):
            raise exceptions.soca_exception(
                error_code=errorcodes.AD_AUTOMATION_PRESET_COMPUTER_FAILED,
                message='Unable to verify cluster node identity: SenderId is required'
            )

        # enforce a nonce for an additional layer of protection against spoofing and help tracing
        nonce = Utils.get_value_as_string('nonce', payload)
        if Utils.is_empty(nonce):
            raise exceptions.soca_exception(
                error_code=errorcodes.AD_AUTOMATION_PRESET_COMPUTER_FAILED,
                message='nonce is required'
            )
        self.nonce = nonce

        # when sent from an EC2 Instance with an IAM Role attached, SenderId is of below format (IAM role ID):
        # AROAZKN2GIY65I74VE5YH:i-035b89c7f49714a3e
        sender_id_tokens = sender_id.split(':')
        if len(sender_id_tokens) != 2:
            raise exceptions.soca_exception(
                error_code=errorcodes.AD_AUTOMATION_PRESET_COMPUTER_FAILED,
                message='Unable to verify cluster node identity: Invalid SenderId'
            )

        instance_id = sender_id_tokens[1]
        try:
            ec2_instances = self.context.aws_util().ec2_describe_instances(filters=[
                {
                    'Name': 'instance-id',
                    'Values': [instance_id]
                },
                {
                    'Name': 'instance-state-name',
                    'Values': ['running']
                }
            ])
        except botocore.exceptions.ClientError as e:
            error_code = str(e.response['Error']['Code'])
            if error_code.startswith('InvalidInstanceID'):
                raise exceptions.soca_exception(
                    error_code=errorcodes.AD_AUTOMATION_PRESET_COMPUTER_FAILED,
                    message=f'Unable to verify cluster node identity: Invalid InstanceId - {instance_id}'
                )
            else:
                # for all other errors, retry
                raise e

        if len(ec2_instances) == 0:
            raise exceptions.soca_exception(
                error_code=errorcodes.AD_AUTOMATION_PRESET_COMPUTER_FAILED,
                message=f'Unable to verify cluster node identity: InstanceId = {instance_id} not found'
            )

        self.instance_id = instance_id

        self.ec2_instance = ec2_instances[0]

        # for Windows instances, there is no way to fetch the hostname from describe instances API.
        # request payload from windows instances will contain hostname. eg. EC2AMAZ-6S29U5P
        hostname = Utils.get_value_as_string('hostname', payload)
        if Utils.is_empty(hostname):
            # Generate and make use of an IDEA hostname
            hostname_data = f"{self.aws_region}|{self.aws_account}|{self.cluster_name}|{self.instance_id}"
            hostname_prefix = self.context.config().get_string('directoryservice.ad_automation.hostname_prefix', default='IDEA-')

            # todo - move to constants
            # todo - change this to produce the shake output and take this many chars vs. bytes (hex)
            # check the configured hostname_prefix length and how much it leaves us for generating the random portion.
            avail_chars = (15 - len(hostname_prefix))
            if avail_chars < 4:
                raise exceptions.soca_exception(
                    error_code=errorcodes.AD_AUTOMATION_PRESET_COMPUTER_FAILED,
                    message=f'{self.log_tag}configured hostname_prefix is too large. Required at least 4 char of random data. Decrease the size of directoryservice.ad_automation.hostname_prefix: {len(hostname_prefix)}'
                )

            self.logger.info(f'Using hostname information {hostname_data} (configured hostname prefix: [{hostname_prefix}] / len {len(hostname_prefix)} / {avail_chars} chars available for random portion)')
            # Take the last n-chars from the resulting shake256 bucket of 256
            shake_value = Utils.shake_256(hostname_data, 256)[(avail_chars * -1):]
            hostname = f'{hostname_prefix}{shake_value}'.upper()
            self.logger.info(f'Generated IDEA hostname / AD hostname of: {hostname} / len {len(hostname)}')

        self.hostname = hostname
        self.logger.info(f'Using hostname for AD join: {self.hostname}')

        self._shell = ShellInvoker(logger=self.logger)

        # verify if adcli is installed and available on the system.
        which_adcli = self._shell.invoke('command -v adcli', shell=True)
        if which_adcli.returncode != 0:
            raise exceptions.general_exception('unable to locate adcli on system to initialize PresetComputerHelper')
        self.ADCLI = which_adcli.stdout

        # Currently adcli uses the GSSAPI SASL mechanism for LDAP authentication and to establish encryption.
        # This should satisfy all requirements set on the server side and LDAPS should only be used if the LDAP
        # port is not accessible due to firewalls or other reasons. Note that connection to the domain controller
        # with LDAPS requires the latest version of adcli (>=0.9.1), which is not available in the Amazon Linux Core x86_64 repo.
        # Only uncomment the lines below to force the LDAPS connection if required:
        # ldap_connection_uri = self.context.config().get_string('directoryservice.ldap_connection_uri', required=True)
        # self.use_ldaps = ldap_connection_uri.startswith('ldaps:')

        self.use_ldaps = False

        # initialize domain controller IP addresses
        self._domain_controller_ips = self.get_domain_controller_ip_addresses()