Path: blob/develop/awscli/customizations/emr/createcluster.py
1567 views
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.1#2# Licensed under the Apache License, Version 2.0 (the "License"). You3# may not use this file except in compliance with the License. A copy of4# the License is located at5#6# http://aws.amazon.com/apache2.0/7#8# or in the "license" file accompanying this file. This file is9# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF10# ANY KIND, either express or implied. See the License for the specific11# language governing permissions and limitations under the License.1213import re1415from botocore.compat import json1617from awscli.customizations.commands import BasicCommand18from awscli.customizations.emr import (19applicationutils,20argumentschema,21constants,22emrfsutils,23emrutils,24exceptions,25hbaseutils,26helptext,27instancefleetsutils,28instancegroupsutils,29steputils,30)31from awscli.customizations.emr.command import Command32from awscli.customizations.emr.constants import EC2_ROLE_NAME, EMR_ROLE_NAME333435class CreateCluster(Command):36NAME = 'create-cluster'37DESCRIPTION = helptext.CREATE_CLUSTER_DESCRIPTION38ARG_TABLE = [39{'name': 'release-label', 'help_text': helptext.RELEASE_LABEL},40{'name': 'os-release-label', 'help_text': helptext.OS_RELEASE_LABEL},41{'name': 'ami-version', 'help_text': helptext.AMI_VERSION},42{43'name': 'instance-groups',44'schema': argumentschema.INSTANCE_GROUPS_SCHEMA,45'help_text': helptext.INSTANCE_GROUPS,46},47{'name': 'instance-type', 'help_text': helptext.INSTANCE_TYPE},48{'name': 'instance-count', 'help_text': helptext.INSTANCE_COUNT},49{50'name': 'auto-terminate',51'action': 'store_true',52'group_name': 'auto_terminate',53'help_text': helptext.AUTO_TERMINATE,54},55{56'name': 'no-auto-terminate',57'action': 'store_true',58'group_name': 'auto_terminate',59},60{61'name': 'instance-fleets',62'schema': argumentschema.INSTANCE_FLEETS_SCHEMA,63'help_text': helptext.INSTANCE_FLEETS,64},65{66'name': 'name',67'default': 'Development Cluster',68'help_text': helptext.CLUSTER_NAME,69},70{'name': 'log-uri', 'help_text': helptext.LOG_URI},71{72'name': 'log-encryption-kms-key-id',73'help_text': helptext.LOG_ENCRYPTION_KMS_KEY_ID,74},75{'name': 'service-role', 'help_text': helptext.SERVICE_ROLE},76{'name': 'auto-scaling-role', 'help_text': helptext.AUTOSCALING_ROLE},77{78'name': 'use-default-roles',79'action': 'store_true',80'help_text': helptext.USE_DEFAULT_ROLES,81},82{'name': 'configurations', 'help_text': helptext.CONFIGURATIONS},83{84'name': 'ec2-attributes',85'help_text': helptext.EC2_ATTRIBUTES,86'schema': argumentschema.EC2_ATTRIBUTES_SCHEMA,87},88{89'name': 'termination-protected',90'action': 'store_true',91'group_name': 'termination_protected',92'help_text': helptext.TERMINATION_PROTECTED,93},94{95'name': 'no-termination-protected',96'action': 'store_true',97'group_name': 'termination_protected',98},99{100'name': 'unhealthy-node-replacement',101'action': 'store_true',102'group_name': 'unhealthy_node_replacement',103'help_text': helptext.UNHEALTHY_NODE_REPLACEMENT,104},105{106'name': 'no-unhealthy-node-replacement',107'action': 'store_true',108'group_name': 'unhealthy_node_replacement',109},110{111'name': 'scale-down-behavior',112'help_text': helptext.SCALE_DOWN_BEHAVIOR,113},114{115'name': 'visible-to-all-users',116'action': 'store_true',117'group_name': 'visibility',118'help_text': helptext.VISIBILITY,119},120{121'name': 'no-visible-to-all-users',122'action': 'store_true',123'group_name': 'visibility',124},125{126'name': 'enable-debugging',127'action': 'store_true',128'group_name': 'debug',129'help_text': helptext.DEBUGGING,130},131{132'name': 'no-enable-debugging',133'action': 'store_true',134'group_name': 'debug',135},136{137'name': 'tags',138'nargs': '+',139'help_text': helptext.TAGS,140'schema': argumentschema.TAGS_SCHEMA,141},142{143'name': 'bootstrap-actions',144'help_text': helptext.BOOTSTRAP_ACTIONS,145'schema': argumentschema.BOOTSTRAP_ACTIONS_SCHEMA,146},147{148'name': 'applications',149'help_text': helptext.APPLICATIONS,150'schema': argumentschema.APPLICATIONS_SCHEMA,151},152{153'name': 'emrfs',154'help_text': helptext.EMR_FS,155'schema': argumentschema.EMR_FS_SCHEMA,156},157{158'name': 'steps',159'schema': argumentschema.STEPS_SCHEMA,160'help_text': helptext.STEPS,161},162{'name': 'additional-info', 'help_text': helptext.ADDITIONAL_INFO},163{164'name': 'restore-from-hbase-backup',165'schema': argumentschema.HBASE_RESTORE_FROM_BACKUP_SCHEMA,166'help_text': helptext.RESTORE_FROM_HBASE,167},168{169'name': 'security-configuration',170'help_text': helptext.SECURITY_CONFIG,171},172{'name': 'custom-ami-id', 'help_text': helptext.CUSTOM_AMI_ID},173{174'name': 'ebs-root-volume-size',175'help_text': helptext.EBS_ROOT_VOLUME_SIZE,176},177{178'name': 'ebs-root-volume-iops',179'help_text': helptext.EBS_ROOT_VOLUME_IOPS,180},181{182'name': 'ebs-root-volume-throughput',183'help_text': helptext.EBS_ROOT_VOLUME_THROUGHPUT,184},185{186'name': 'repo-upgrade-on-boot',187'help_text': helptext.REPO_UPGRADE_ON_BOOT,188},189{190'name': 'kerberos-attributes',191'schema': argumentschema.KERBEROS_ATTRIBUTES_SCHEMA,192'help_text': helptext.KERBEROS_ATTRIBUTES,193},194{195'name': 'step-concurrency-level',196'cli_type_name': 'integer',197'help_text': helptext.STEP_CONCURRENCY_LEVEL,198},199{200'name': 'managed-scaling-policy',201'schema': argumentschema.MANAGED_SCALING_POLICY_SCHEMA,202'help_text': helptext.MANAGED_SCALING_POLICY,203},204{205'name': 'placement-group-configs',206'schema': argumentschema.PLACEMENT_GROUP_CONFIGS_SCHEMA,207'help_text': helptext.PLACEMENT_GROUP_CONFIGS,208},209{210'name': 'auto-termination-policy',211'schema': argumentschema.AUTO_TERMINATION_POLICY_SCHEMA,212'help_text': helptext.AUTO_TERMINATION_POLICY,213},214{215'name': 'extended-support',216'action': 'store_true',217'group_name': 'extended-support',218'help_text': helptext.EXTENDED_SUPPORT,219},220{221'name': 'no-extended-support',222'action': 'store_true',223'group_name': 'extended-support',224},225]226SYNOPSIS = BasicCommand.FROM_FILE('emr', 'create-cluster-synopsis.txt')227EXAMPLES = BasicCommand.FROM_FILE('emr', 'create-cluster-examples.rst')228229def _run_main_command(self, parsed_args, parsed_globals):230params = {}231params['Name'] = parsed_args.name232233self._validate_release_label_ami_version(parsed_args)234235service_role_validation_message = (236" Either choose --use-default-roles or use both --service-role "237"<roleName> and --ec2-attributes InstanceProfile=<profileName>."238)239240if (241parsed_args.use_default_roles is True242and parsed_args.service_role is not None243):244raise exceptions.MutualExclusiveOptionError(245option1="--use-default-roles",246option2="--service-role",247message=service_role_validation_message,248)249250if (251parsed_args.use_default_roles is True252and parsed_args.ec2_attributes is not None253and 'InstanceProfile' in parsed_args.ec2_attributes254):255raise exceptions.MutualExclusiveOptionError(256option1="--use-default-roles",257option2="--ec2-attributes InstanceProfile",258message=service_role_validation_message,259)260261if (262parsed_args.instance_groups is not None263and parsed_args.instance_fleets is not None264):265raise exceptions.MutualExclusiveOptionError(266option1="--instance-groups", option2="--instance-fleets"267)268269instances_config = {}270if parsed_args.instance_fleets is not None:271instances_config['InstanceFleets'] = (272instancefleetsutils.validate_and_build_instance_fleets(273parsed_args.instance_fleets274)275)276else:277instances_config['InstanceGroups'] = (278instancegroupsutils.validate_and_build_instance_groups(279instance_groups=parsed_args.instance_groups,280instance_type=parsed_args.instance_type,281instance_count=parsed_args.instance_count,282)283)284285if parsed_args.release_label is not None:286params["ReleaseLabel"] = parsed_args.release_label287if parsed_args.configurations is not None:288try:289params["Configurations"] = json.loads(290parsed_args.configurations291)292except ValueError:293raise ValueError(294'aws: error: invalid json argument for '295'option --configurations'296)297298if (299parsed_args.release_label is None300and parsed_args.ami_version is not None301):302is_valid_ami_version = re.match(303r'\d?\..*', parsed_args.ami_version304)305if is_valid_ami_version is None:306raise exceptions.InvalidAmiVersionError(307ami_version=parsed_args.ami_version308)309params['AmiVersion'] = parsed_args.ami_version310emrutils.apply_dict(311params, 'AdditionalInfo', parsed_args.additional_info312)313emrutils.apply_dict(params, 'LogUri', parsed_args.log_uri)314315if parsed_args.os_release_label is not None:316emrutils.apply_dict(317params, 'OSReleaseLabel', parsed_args.os_release_label318)319320if parsed_args.log_encryption_kms_key_id is not None:321emrutils.apply_dict(322params,323'LogEncryptionKmsKeyId',324parsed_args.log_encryption_kms_key_id,325)326327if parsed_args.use_default_roles is True:328parsed_args.service_role = EMR_ROLE_NAME329if parsed_args.ec2_attributes is None:330parsed_args.ec2_attributes = {}331parsed_args.ec2_attributes['InstanceProfile'] = EC2_ROLE_NAME332333emrutils.apply_dict(params, 'ServiceRole', parsed_args.service_role)334335if parsed_args.instance_groups is not None:336for instance_group in instances_config['InstanceGroups']:337if 'AutoScalingPolicy' in instance_group.keys():338if parsed_args.auto_scaling_role is None:339raise exceptions.MissingAutoScalingRoleError()340341emrutils.apply_dict(342params, 'AutoScalingRole', parsed_args.auto_scaling_role343)344345if parsed_args.scale_down_behavior is not None:346emrutils.apply_dict(347params, 'ScaleDownBehavior', parsed_args.scale_down_behavior348)349350if (351parsed_args.no_auto_terminate is False352and parsed_args.auto_terminate is False353):354parsed_args.no_auto_terminate = True355356instances_config['KeepJobFlowAliveWhenNoSteps'] = (357emrutils.apply_boolean_options(358parsed_args.no_auto_terminate,359'--no-auto-terminate',360parsed_args.auto_terminate,361'--auto-terminate',362)363)364365instances_config['TerminationProtected'] = (366emrutils.apply_boolean_options(367parsed_args.termination_protected,368'--termination-protected',369parsed_args.no_termination_protected,370'--no-termination-protected',371)372)373374if (375parsed_args.unhealthy_node_replacement376or parsed_args.no_unhealthy_node_replacement377):378instances_config['UnhealthyNodeReplacement'] = (379emrutils.apply_boolean_options(380parsed_args.unhealthy_node_replacement,381'--unhealthy-node-replacement',382parsed_args.no_unhealthy_node_replacement,383'--no-unhealthy-node-replacement',384)385)386387if (388parsed_args.visible_to_all_users is False389and parsed_args.no_visible_to_all_users is False390):391parsed_args.visible_to_all_users = True392393params['VisibleToAllUsers'] = emrutils.apply_boolean_options(394parsed_args.visible_to_all_users,395'--visible-to-all-users',396parsed_args.no_visible_to_all_users,397'--no-visible-to-all-users',398)399400params['Tags'] = emrutils.parse_tags(parsed_args.tags)401params['Instances'] = instances_config402403if parsed_args.ec2_attributes is not None:404self._build_ec2_attributes(405cluster=params, parsed_attrs=parsed_args.ec2_attributes406)407408debugging_enabled = emrutils.apply_boolean_options(409parsed_args.enable_debugging,410'--enable-debugging',411parsed_args.no_enable_debugging,412'--no-enable-debugging',413)414415if parsed_args.log_uri is None and debugging_enabled is True:416raise exceptions.LogUriError417418if debugging_enabled is True:419self._update_cluster_dict(420cluster=params,421key='Steps',422value=[423self._build_enable_debugging(parsed_args, parsed_globals)424],425)426427if parsed_args.applications is not None:428if parsed_args.release_label is None:429app_list, ba_list, step_list = (430applicationutils.build_applications(431region=self.region,432parsed_applications=parsed_args.applications,433ami_version=params['AmiVersion'],434)435)436self._update_cluster_dict(437params, 'NewSupportedProducts', app_list438)439self._update_cluster_dict(params, 'BootstrapActions', ba_list)440self._update_cluster_dict(params, 'Steps', step_list)441else:442params["Applications"] = []443for application in parsed_args.applications:444params["Applications"].append(application)445446hbase_restore_config = parsed_args.restore_from_hbase_backup447if hbase_restore_config is not None:448args = hbaseutils.build_hbase_restore_from_backup_args(449dir=hbase_restore_config.get('Dir'),450backup_version=hbase_restore_config.get('BackupVersion'),451)452step_config = emrutils.build_step(453jar=constants.HBASE_JAR_PATH,454name=constants.HBASE_RESTORE_STEP_NAME,455action_on_failure=constants.CANCEL_AND_WAIT,456args=args,457)458self._update_cluster_dict(params, 'Steps', [step_config])459460if parsed_args.bootstrap_actions is not None:461self._build_bootstrap_actions(462cluster=params,463parsed_boostrap_actions=parsed_args.bootstrap_actions,464)465466if parsed_args.emrfs is not None:467self._handle_emrfs_parameters(468cluster=params,469emrfs_args=parsed_args.emrfs,470release_label=parsed_args.release_label,471)472473if parsed_args.steps is not None:474steps_list = steputils.build_step_config_list(475parsed_step_list=parsed_args.steps,476region=self.region,477release_label=parsed_args.release_label,478)479self._update_cluster_dict(480cluster=params, key='Steps', value=steps_list481)482483if parsed_args.security_configuration is not None:484emrutils.apply_dict(485params,486'SecurityConfiguration',487parsed_args.security_configuration,488)489490if parsed_args.custom_ami_id is not None:491emrutils.apply_dict(492params, 'CustomAmiId', parsed_args.custom_ami_id493)494if parsed_args.ebs_root_volume_size is not None:495emrutils.apply_dict(496params,497'EbsRootVolumeSize',498int(parsed_args.ebs_root_volume_size),499)500if parsed_args.ebs_root_volume_iops is not None:501emrutils.apply_dict(502params,503'EbsRootVolumeIops',504int(parsed_args.ebs_root_volume_iops),505)506if parsed_args.ebs_root_volume_throughput is not None:507emrutils.apply_dict(508params,509'EbsRootVolumeThroughput',510int(parsed_args.ebs_root_volume_throughput),511)512513if parsed_args.repo_upgrade_on_boot is not None:514emrutils.apply_dict(515params, 'RepoUpgradeOnBoot', parsed_args.repo_upgrade_on_boot516)517518if parsed_args.kerberos_attributes is not None:519emrutils.apply_dict(520params, 'KerberosAttributes', parsed_args.kerberos_attributes521)522523if parsed_args.step_concurrency_level is not None:524params['StepConcurrencyLevel'] = parsed_args.step_concurrency_level525526if parsed_args.extended_support or parsed_args.no_extended_support:527params['ExtendedSupport'] = emrutils.apply_boolean_options(528parsed_args.extended_support,529'--extended-support',530parsed_args.no_extended_support,531'--no-extended-support',532)533534if parsed_args.managed_scaling_policy is not None:535emrutils.apply_dict(536params,537'ManagedScalingPolicy',538parsed_args.managed_scaling_policy,539)540541if parsed_args.placement_group_configs is not None:542emrutils.apply_dict(543params,544'PlacementGroupConfigs',545parsed_args.placement_group_configs,546)547548if parsed_args.auto_termination_policy is not None:549emrutils.apply_dict(550params,551'AutoTerminationPolicy',552parsed_args.auto_termination_policy,553)554555self._validate_required_applications(parsed_args)556557run_job_flow_response = emrutils.call(558self._session,559'run_job_flow',560params,561self.region,562parsed_globals.endpoint_url,563parsed_globals.verify_ssl,564)565566constructed_result = self._construct_result(run_job_flow_response)567emrutils.display_response(568self._session, 'run_job_flow', constructed_result, parsed_globals569)570571return 0572573def _construct_result(self, run_job_flow_result):574jobFlowId = None575clusterArn = None576if run_job_flow_result is not None:577jobFlowId = run_job_flow_result.get('JobFlowId')578clusterArn = run_job_flow_result.get('ClusterArn')579580if jobFlowId is not None:581return {'ClusterId': jobFlowId, 'ClusterArn': clusterArn}582else:583return {}584585def _build_ec2_attributes(self, cluster, parsed_attrs):586keys = parsed_attrs.keys()587instances = cluster['Instances']588589if 'SubnetId' in keys and 'SubnetIds' in keys:590raise exceptions.MutualExclusiveOptionError(591option1="SubnetId", option2="SubnetIds"592)593594if 'AvailabilityZone' in keys and 'AvailabilityZones' in keys:595raise exceptions.MutualExclusiveOptionError(596option1="AvailabilityZone", option2="AvailabilityZones"597)598599if ('SubnetId' in keys or 'SubnetIds' in keys) and (600'AvailabilityZone' in keys or 'AvailabilityZones' in keys601):602raise exceptions.SubnetAndAzValidationError603604emrutils.apply_params(605src_params=parsed_attrs,606src_key='KeyName',607dest_params=instances,608dest_key='Ec2KeyName',609)610emrutils.apply_params(611src_params=parsed_attrs,612src_key='SubnetId',613dest_params=instances,614dest_key='Ec2SubnetId',615)616emrutils.apply_params(617src_params=parsed_attrs,618src_key='SubnetIds',619dest_params=instances,620dest_key='Ec2SubnetIds',621)622623if 'AvailabilityZone' in keys:624instances['Placement'] = dict()625emrutils.apply_params(626src_params=parsed_attrs,627src_key='AvailabilityZone',628dest_params=instances['Placement'],629dest_key='AvailabilityZone',630)631632if 'AvailabilityZones' in keys:633instances['Placement'] = dict()634emrutils.apply_params(635src_params=parsed_attrs,636src_key='AvailabilityZones',637dest_params=instances['Placement'],638dest_key='AvailabilityZones',639)640641emrutils.apply_params(642src_params=parsed_attrs,643src_key='InstanceProfile',644dest_params=cluster,645dest_key='JobFlowRole',646)647648emrutils.apply_params(649src_params=parsed_attrs,650src_key='EmrManagedMasterSecurityGroup',651dest_params=instances,652dest_key='EmrManagedMasterSecurityGroup',653)654655emrutils.apply_params(656src_params=parsed_attrs,657src_key='EmrManagedSlaveSecurityGroup',658dest_params=instances,659dest_key='EmrManagedSlaveSecurityGroup',660)661662emrutils.apply_params(663src_params=parsed_attrs,664src_key='ServiceAccessSecurityGroup',665dest_params=instances,666dest_key='ServiceAccessSecurityGroup',667)668669emrutils.apply_params(670src_params=parsed_attrs,671src_key='AdditionalMasterSecurityGroups',672dest_params=instances,673dest_key='AdditionalMasterSecurityGroups',674)675676emrutils.apply_params(677src_params=parsed_attrs,678src_key='AdditionalSlaveSecurityGroups',679dest_params=instances,680dest_key='AdditionalSlaveSecurityGroups',681)682683emrutils.apply(params=cluster, key='Instances', value=instances)684685return cluster686687def _build_bootstrap_actions(self, cluster, parsed_boostrap_actions):688cluster_ba_list = cluster.get('BootstrapActions')689if cluster_ba_list is None:690cluster_ba_list = []691692bootstrap_actions = []693if (694len(cluster_ba_list) + len(parsed_boostrap_actions)695> constants.MAX_BOOTSTRAP_ACTION_NUMBER696):697raise ValueError(698'aws: error: maximum number of '699'bootstrap actions for a cluster exceeded.'700)701702for ba in parsed_boostrap_actions:703ba_config = {}704if ba.get('Name') is not None:705ba_config['Name'] = ba.get('Name')706else:707ba_config['Name'] = constants.BOOTSTRAP_ACTION_NAME708script_arg_config = {}709emrutils.apply_params(710src_params=ba,711src_key='Path',712dest_params=script_arg_config,713dest_key='Path',714)715emrutils.apply_params(716src_params=ba,717src_key='Args',718dest_params=script_arg_config,719dest_key='Args',720)721emrutils.apply(722params=ba_config,723key='ScriptBootstrapAction',724value=script_arg_config,725)726bootstrap_actions.append(ba_config)727728result = cluster_ba_list + bootstrap_actions729if result:730cluster['BootstrapActions'] = result731732return cluster733734def _build_enable_debugging(self, parsed_args, parsed_globals):735if parsed_args.release_label:736jar = constants.COMMAND_RUNNER737args = [constants.DEBUGGING_COMMAND]738else:739jar = emrutils.get_script_runner(self.region)740args = [741emrutils.build_s3_link(742relative_path=constants.DEBUGGING_PATH, region=self.region743)744]745746return emrutils.build_step(747name=constants.DEBUGGING_NAME,748action_on_failure=constants.TERMINATE_CLUSTER,749jar=jar,750args=args,751)752753def _update_cluster_dict(self, cluster, key, value):754if key in cluster:755cluster[key] += value756elif value:757cluster[key] = value758return cluster759760def _validate_release_label_ami_version(self, parsed_args):761if (762parsed_args.ami_version is not None763and parsed_args.release_label is not None764):765raise exceptions.MutualExclusiveOptionError(766option1="--ami-version", option2="--release-label"767)768769if (770parsed_args.ami_version is None771and parsed_args.release_label is None772):773raise exceptions.RequiredOptionsError(774option1="--ami-version", option2="--release-label"775)776777# Checks if the applications required by steps are specified778# using the --applications option.779def _validate_required_applications(self, parsed_args):780specified_apps = set([])781if parsed_args.applications is not None:782specified_apps = set(783[app['Name'].lower() for app in parsed_args.applications]784)785786missing_apps = self._get_missing_applications_for_steps(787specified_apps, parsed_args788)789# Check for HBase.790if parsed_args.restore_from_hbase_backup is not None:791if constants.HBASE not in specified_apps:792missing_apps.add(constants.HBASE.title())793794if missing_apps:795raise exceptions.MissingApplicationsError(796applications=missing_apps797)798799def _get_missing_applications_for_steps(self, specified_apps, parsed_args):800allowed_app_steps = set(801[constants.HIVE, constants.PIG, constants.IMPALA]802)803missing_apps = set()804if parsed_args.steps is not None:805for step in parsed_args.steps:806if len(missing_apps) == len(allowed_app_steps):807break808step_type = step.get('Type')809810if step_type is not None:811step_type = step_type.lower()812if (813step_type in allowed_app_steps814and step_type not in specified_apps815):816missing_apps.add(step['Type'].title())817return missing_apps818819def _filter_configurations_in_special_cases(820self, configurations, parsed_args, parsed_configs821):822if parsed_args.use_default_roles:823configurations = [824x825for x in configurations826if x.name != 'service_role' and x.name != 'instance_profile'827]828return configurations829830def _handle_emrfs_parameters(self, cluster, emrfs_args, release_label):831if release_label:832self.validate_no_emrfs_configuration(cluster)833emrfs_configuration = emrfsutils.build_emrfs_confiuration(834emrfs_args835)836837self._update_cluster_dict(838cluster=cluster,839key='Configurations',840value=[emrfs_configuration],841)842else:843emrfs_ba_config_list = emrfsutils.build_bootstrap_action_configs(844self.region, emrfs_args845)846self._update_cluster_dict(847cluster=cluster,848key='BootstrapActions',849value=emrfs_ba_config_list,850)851852def validate_no_emrfs_configuration(self, cluster):853if 'Configurations' in cluster:854for config in cluster['Configurations']:855if (856config is not None857and config.get('Classification') == constants.EMRFS_SITE858):859raise exceptions.DuplicateEmrFsConfigurationError860861862