def ValidateServiceMutexConfig(unused_ref, unused_args, req): """Validates that the mutual exclusive configurations of Dataproc Metastore service are not set at the same time. Args: req: A request with `service` field. Returns: A request without service mutex configuration conflicts. Raises: BadArgumentException: when mutual exclusive configurations of service are set at the same time. """ if (req.service.encryptionConfig and req.service.encryptionConfig.kmsKey and req.service.metadataIntegration.dataCatalogConfig.enabled): raise exceptions.BadArgumentException( '--data-catalog-sync', 'Data Catalog synchronization cannot be used in conjunction with customer-managed encryption keys.' ) if (req.service.hiveMetastoreConfig and req.service.hiveMetastoreConfig.kerberosConfig and req.service.hiveMetastoreConfig.kerberosConfig.principal and _IsNetworkConfigPresentInService(req.service)): raise exceptions.BadArgumentException( '--kerberos-principal', 'Kerberos configuration cannot be used in conjunction with --network-config-from-file or --consumer-subnetworks.' ) if (req.service.hiveMetastoreConfig and req.service.hiveMetastoreConfig.auxiliaryVersions and _IsNetworkConfigPresentInService(req.service)): raise exceptions.BadArgumentException( '--auxiliary-versions', 'Auxiliary versions configuration cannot be used in conjunction with --network-config-from-file or --consumer-subnetworks.' ) return req
def Run(self, args): # If zone is not set, retrieve the one from the config. if args.zone is None: args.zone = properties.VALUES.compute.zone.Get(required=True) # Retrieve the TPU node. tpu_name_ref = args.CONCEPTS.tpu.Parse() tpu = tpu_utils.TPUNode(self.ReleaseTrack()) node = tpu.Get(tpu_name_ref.Name(), args.zone) if not tpu_utils.IsTPUVMNode(node): raise exceptions.BadArgumentException( 'TPU', 'this command is only available for Cloud TPU VM nodes. To access ' 'this node, please see ' 'https://cloud.google.com/tpu/docs/creating-deleting-tpus.') if not node.dataDisks: raise exceptions.BadArgumentException( 'TPU', 'no data disks to detach from current TPU VM.') source_path_match = re.match( r'projects/.+/(zones|regions)/.+/disks/.+', args.disk) if source_path_match: source_path = args.disk else: project = properties.VALUES.core.project.Get(required=True) source_path = 'projects/' + project + '/zones/' + args.zone + '/disks/' + args.disk source_disk_list = [] for disk in node.dataDisks: source_disk_list.append(disk.sourceDisk) for i, source_disk in enumerate(source_disk_list): if source_path != source_disk: continue if source_path == source_disk: del node.dataDisks[i] break else: raise exceptions.BadArgumentException( 'TPU', 'error: specified data disk is not currently attached to the TPU VM.' ) operation = tpu.UpdateNode(tpu_name_ref.Name(), args.zone, node, 'data_disks') operation_ref = resources.REGISTRY.ParseRelativeName( operation.name, collection='tpu.projects.locations.operations') return tpu.WaitForOperation(operation_ref, 'Detaching disk from TPU VM')
def _CreateRequest(self, args): messages = cloudkms_base.GetMessagesModule() ekm_connection_ref = args.CONCEPTS.ekm_connection.Parse() parent_ref = ekm_connection_ref.Parent() certificate_list = [] for cert_file in args.server_certificates_files: try: certificate_list.append( messages.Certificate( rawDer=certs.GetDerCertificate(cert_file))) except Exception as e: raise exceptions.BadArgumentException( '--server-certificates-files', 'Error while attempting to read file {} : {}'.format( cert_file, e)) req = messages.CloudkmsProjectsLocationsEkmConnectionsCreateRequest( parent=parent_ref.RelativeName(), ekmConnectionId=ekm_connection_ref.Name(), ekmConnection=messages.EkmConnection(serviceResolvers=[ messages.ServiceResolver( serviceDirectoryService=args.service_directory_service, endpointFilter=args.endpoint_filter, hostname=args.hostname, serverCertificates=certificate_list) ])) return req
def ClearMaintenanceWindow(ref, args, request): """Clears cluster.maintenance_policy in the request if --clear-maintenance-window flag is specified. Args: ref: reference to the cluster object. args: command line arguments. request: API request to be issued Returns: modified request """ del ref # unused argument if not flags.FlagIsExplicitlySet(args, "clear_maintenance_window"): return request if not args.clear_maintenance_window: raise exceptions.BadArgumentException("--no-clear-maintenance-window", "The flag is not supported") if request.cluster is None: release_track = args.calliope_command.ReleaseTrack() request.cluster = util.GetMessagesModule(release_track).Cluster() request.cluster.maintenancePolicy = None _AddFieldToUpdateMask("maintenancePolicy", request) return request
def ValidatePort(port): """Python hook to validate that the port is between 1024 and 65535, inclusive.""" if port < 1024 or port > 65535: raise exceptions.BadArgumentException( '--port', 'Port ({0}) is not in the range [1025, 65535].'.format(port)) return port
def _ReadOrFetchPublicKeyBytes(self, args, import_job_name): client = cloudkms_base.GetClientInstance() messages = cloudkms_base.GetMessagesModule() # If the public key was provided, read it off disk. Otherwise, fetch it from # KMS. public_key_bytes = None if args.public_key_file: try: public_key_bytes = self._ReadFile(args.public_key_file, max_bytes=65536) except files.Error as e: raise exceptions.BadFileException( 'Failed to read public key file [{0}]: {1}'.format( args.public_key_file, e)) else: import_job = client.projects_locations_keyRings_importJobs.Get( # pylint: disable=line-too-long messages.CloudkmsProjectsLocationsKeyRingsImportJobsGetRequest( name=import_job_name)) if import_job.state != messages.ImportJob.StateValueValuesEnum.ACTIVE: raise exceptions.BadArgumentException( 'import-job', 'Import job [{0}] is not active (state is {1}).'.format( import_job_name, import_job.state)) public_key_bytes = import_job.publicKey.pem.encode('ascii') return public_key_bytes
def ValidateHourOfDay(hour): """Validates that the hour falls between 0 and 23, inclusive.""" if hour < 0 or hour > 23: raise exceptions.BadArgumentException( '--maintenance-window-hour-of-day', 'Hour of day ({0}) is not in [0, 23].'.format(hour)) return hour
def CreateRequest(self, args, messages, previous_ekm_connection): ec_ref = flags.ParseEkmConnectionName(args) service_resolver_to_update = previous_ekm_connection.serviceResolvers[0] if args.service_directory_service: service_resolver_to_update.serviceDirectoryService = args.service_directory_service if args.endpoint_filter: service_resolver_to_update.endpointFilter = args.endpoint_filter if args.hostname: service_resolver_to_update.hostname = args.hostname certificate_list = [] if args.server_certificates_files: for cert_file in args.server_certificates_files: try: certificate_list.append( messages.Certificate(rawDer=certs.GetDerCertificate(cert_file))) except Exception as e: raise exceptions.BadArgumentException( '--server-certificates-files', 'Error while attempting to read file {} : {}'.format( cert_file, e)) service_resolver_to_update.serverCertificates = certificate_list req = messages.CloudkmsProjectsLocationsEkmConnectionsPatchRequest( name=ec_ref.RelativeName(), ekmConnection=messages.EkmConnection( serviceResolvers=[service_resolver_to_update])) req.updateMask = 'serviceResolvers' return req
def _GetNamesAndAddresses(self, args): """Returns names and addresses provided in args.""" if not args.addresses and not args.name: raise exceptions.MinimumArgumentException( ['NAME', '--address'], 'At least one name or address must be provided.') if args.name: names = args.name else: # If we dont have any names then we must some addresses. names = [name_generator.GenerateRandomName() for _ in args.addresses] if args.addresses: addresses = args.addresses else: # If we dont have any addresses then we must some names. addresses = [None] * len(args.name) if len(addresses) != len(names): raise exceptions.BadArgumentException( '--addresses', 'If providing both, you must specify the same number of names as ' 'addresses.') return names, addresses
def GetDiskOverride(messages, stateful_disk, disk_getter): """Prepares disk override message, combining with params from the instance.""" if stateful_disk.get('source'): source = stateful_disk.get('source') mode = stateful_disk.get('mode', 'rw') else: disk = disk_getter.get_disk( device_name=stateful_disk.get('device-name')) if disk is None: if disk_getter.instance_exists: error_message = ( '[source] must be given while defining stateful disks' ' in instance configs for non existing disks in given' ' instance') else: error_message = ( '[source] must be given while defining stateful disks' ' in instance configs for non existing instances') raise exceptions.BadArgumentException('source', error_message) source = disk.source mode = disk.mode return messages.ManagedInstanceOverrideDiskOverride( deviceName=stateful_disk.get('device-name'), source=source, mode=GetMode(messages, mode), )
def _GetDiskSource(self, instance, device): for d in instance.disks: if d.deviceName == device: return d.source raise exceptions.BadArgumentException( '--stateful-disks', 'The instance doesn\'t have a disk with specified device name.')
def _BuildExplanationSpec(self, args): parameters = None method = args.explanation_method if not method: return None if method.lower() == 'integrated-gradients': parameters = ( self.messages.GoogleCloudAiplatformV1beta1ExplanationParameters( integratedGradientsAttribution=self.messages .GoogleCloudAiplatformV1beta1IntegratedGradientsAttribution( stepCount=args.explanation_step_count, smoothGradConfig=self._BuildSmoothGradConfig(args)))) elif method.lower() == 'xrai': parameters = ( self.messages.GoogleCloudAiplatformV1beta1ExplanationParameters( xraiAttribution=self.messages .GoogleCloudAiplatformV1beta1XraiAttribution( stepCount=args.explanation_step_count, smoothGradConfig=self._BuildSmoothGradConfig(args)))) elif method.lower() == 'sampled-shapley': parameters = ( self.messages.GoogleCloudAiplatformV1beta1ExplanationParameters( sampledShapleyAttribution=self.messages .GoogleCloudAiplatformV1beta1SampledShapleyAttribution( pathCount=args.explanation_path_count))) else: raise gcloud_exceptions.BadArgumentException( '--explanation-method', 'Explanation method must be one of `integrated-gradients`, ' '`xrai` and `sampled-shapley`.') return self.messages.GoogleCloudAiplatformV1beta1ExplanationSpec( metadata=self._ReadExplanationMetadata(args.explanation_metadata_file), parameters=parameters)
def MakePreservedStateDiskEntry(messages, stateful_disk_data, disk_getter): """Prepares disk preserved state entry, combining with params from the instance.""" if stateful_disk_data.get('source'): source = stateful_disk_data.get('source') mode = stateful_disk_data.get('mode', 'rw') else: disk = disk_getter.get_disk( device_name=stateful_disk_data.get('device-name')) if disk is None: if disk_getter.instance_exists: error_message = ('[source] must be given while defining stateful disks' ' in instance configs for non existing disks in given' ' instance') else: error_message = ('[source] must be given while defining stateful disks' ' in instance configs for non existing instances') raise exceptions.BadArgumentException('source', error_message) source = disk.source mode = disk.mode preserved_disk = \ messages.PreservedStatePreservedDisk( autoDelete=(stateful_disk_data.get('auto-delete') or AutoDeleteFlag.NEVER).GetAutoDeleteEnumValue( messages.PreservedStatePreservedDisk .AutoDeleteValueValuesEnum), source=source, mode=GetMode(messages, mode)) return messages.PreservedState.DisksValue.AdditionalProperty( key=stateful_disk_data.get('device-name'), value=preserved_disk)
def ValidatePlatformIsManaged(platform): if platform != 'managed': raise calliope_exceptions.BadArgumentException( '--platform', 'The platform [{}] is not supported by this operation. ' 'Specify `--platform managed` or run ' '`gcloud config set run/platform managed`.'.format(platform)) return platform
def _SetKmsKey(self, secret_ref, secret, kms_key, location): if secret.replication.automatic: if location: raise exceptions.BadArgumentException( 'location', self.LOCATION_AND_AUTOMATIC_MESSAGE) updated_secret = secrets_api.Secrets().SetReplication( secret_ref, 'automatic', [], [kms_key]) secrets_log.Secrets().UpdatedReplication(secret_ref) return updated_secret if secret.replication.userManaged and secret.replication.userManaged.replicas: if not location: raise exceptions.RequiredArgumentException( 'location', self.LOCATION_REQUIRED_MESSAGE) locations = [] keys = [] found_location = False for replica in secret.replication.userManaged.replicas: if not replica.location: raise exceptions.ToolException(self.MISCONFIGURED_REPLICATION_MESSAGE) locations.append(replica.location) if location == replica.location: found_location = True keys.append(kms_key) elif replica.customerManagedEncryption and replica.customerManagedEncryption.kmsKeyName: keys.append(replica.customerManagedEncryption.kmsKeyName) if not found_location: raise exceptions.InvalidArgumentException( 'location', self.LOCATION_NOT_IN_POLICY_MESSAGE) if len(locations) != len(keys): raise exceptions.ToolException(self.PARTIALLY_CMEK_MESSAGE) updated_secret = secrets_api.Secrets().SetReplication( secret_ref, 'user-managed', locations, keys) secrets_log.Secrets().UpdatedReplication(secret_ref) return updated_secret raise exceptions.ToolException(self.MISCONFIGURED_REPLICATION_MESSAGE)
def _CreateInstanceGroupManager( self, args, group_ref, template_ref, client, holder): """Create parts of Instance Group Manager shared for the track.""" if (group_ref.Collection() != 'compute.instanceGroupManagers' and args.IsSpecified('stateful_disks')): raise exceptions.BadArgumentException( '--stateful-disks', 'Allowed only with zonal managed instance groups.') instance_groups_flags.ValidateManagedInstanceGroupScopeArgs( args, holder.resources) health_check = managed_instance_groups_utils.GetHealthCheckUri( holder.resources, args, self.HEALTH_CHECK_ARG) return client.messages.InstanceGroupManager( name=group_ref.Name(), description=args.description, instanceTemplate=template_ref.SelfLink(), baseInstanceName=self._GetInstanceGroupManagerBaseInstanceName( args.base_instance_name, group_ref), targetPools=self._GetInstanceGroupManagerTargetPools( args.target_pool, group_ref, holder), targetSize=int(args.size), autoHealingPolicies=( managed_instance_groups_utils.CreateAutohealingPolicies( client.messages, health_check, args.initial_delay)), distributionPolicy=self._CreateDistributionPolicy( args.zones, holder.resources, client.messages), statefulPolicy=self._GetStatefulPolicy(args.stateful_disks, client), )
def ValidateKerberosPrincipal(kerberos_principal): pattern = re.compile(r'^(.+)/(.+)@(.+)$') if not pattern.match(kerberos_principal): raise exceptions.BadArgumentException( '--kerberos-principal', 'Kerberos Principal {0} does not match ReGeX {1}.'.format( kerberos_principal, pattern)) return kerberos_principal
def ComparisonValidator(if_value): if if_value.lower() == 'absent': return (None, None) if_value = if_value.split() if len(if_value) != 2: raise exceptions.BadArgumentException('--if', 'Invalid value for flag.') try: comparator = COMPARISON_TO_ENUM[if_value[0]] threshold_value = float(if_value[1]) return comparator, threshold_value except KeyError: raise exceptions.BadArgumentException('--if', 'Comparator must be < or >.') except ValueError: raise exceptions.BadArgumentException('--if', 'Threshold not a value float.')
def Run(self, args): zones_client = managed_zones.Client.FromApiVersion('v1') zone_ref = args.CONCEPTS.zone.Parse() # This is a special case in that the . and .. mess up the URI in the HTTP # request. All other bad arguments are handled server side. if zone_ref.managedZone == '.' or zone_ref.managedZone == '..': raise exceptions.BadArgumentException('describe', zone_ref.managedZone) return zones_client.Get(zone_ref)
def _ExtractDescriptionAndAgentRules(guest_policy_description): """Extract Ops Agents policy's description and agent rules. Extract Ops Agents policy's description and agent rules from description of OS Config guest policy. Args: guest_policy_description: OS Config guest policy's description. Returns: extracted description and agent rules for ops agents policy. Raises: BadArgumentException: If guest policy's description is illformed JSON object, or if it does not have keys description or agentRules. """ try: decode_description = json.loads(guest_policy_description) except ValueError as e: raise exceptions.BadArgumentException( 'description', 'description field is not a JSON object: {}'.format(e)) if not isinstance(decode_description, dict): raise exceptions.BadArgumentException( 'description', 'description field is not a JSON object.') try: decoded_description = decode_description['description'] except KeyError as e: raise exceptions.BadArgumentException( 'description.description', 'missing a required key description: %s' % e) try: decoded_agent_rules = decode_description['agentRules'] except KeyError as e: raise exceptions.BadArgumentException( 'description.agentRules', 'missing a required key agentRules: %s' % e) return (decoded_description, decoded_agent_rules)
def _ReadIndexMetadata(self, metadata_file): """Parse json metadata file.""" if not metadata_file: raise gcloud_exceptions.BadArgumentException( '--metadata-file', 'Index metadata file must be specified.') index_metadata = None # Yaml is a superset of json, so parse json file as yaml. data = yaml.load_path(metadata_file) if data: index_metadata = messages_util.DictToMessageWithErrorCheck( data, extra_types.JsonValue) return index_metadata
def _ReadExplanationMetadata(self, explanation_metadata_file): explanation_metadata = None if not explanation_metadata_file: raise gcloud_exceptions.BadArgumentException( '--explanation-metadata-file', 'Explanation metadata file must be specified.') # Yaml is a superset of json, so parse json file as yaml. data = yaml.load_path(explanation_metadata_file) if data: explanation_metadata = messages_util.DictToMessageWithErrorCheck( data, self.messages.GoogleCloudAiplatformV1beta1ExplanationMetadata) return explanation_metadata
def GetDerCertificate(certificate_file): """Read certificate_file and return the certificate in DER encoding. Args: certificate_file: A file handle to the certificate in PEM or DER format. Returns: The certificate in DER encoding. Raises: BadArgumentException: The provided certificate failed to parse as a PEM. """ data = files.ReadBinaryFileContents(certificate_file) if b'-----BEGIN CERTIFICATE-----' in data: certb64 = data.replace(b'-----BEGIN CERTIFICATE-----', b'', 1) certb64 = certb64.replace(b'-----END CERTIFICATE-----', b'', 1) # If there's another certificate detected afterwards if b'-----BEGIN CERTIFICATE-----' in certb64: raise exceptions.BadArgumentException( 'certificate_file', 'Cannot place multiple certificates in the same file : {}'. format(certificate_file)) try: certb64 = certb64.replace(b'\r', b'').replace(b'\n', b'') # Since validate=True isn't supported on Python2's base64.b64decode, # re-encode output and compare to original. decoded = base64.b64decode(six.ensure_binary(certb64)) encoded = base64.b64encode(decoded) if encoded != certb64: raise ValueError('Non-base64 digit found.') except Exception as e: raise exceptions.BadArgumentException( 'certificate_file', 'Recognized {} as a PEM file but failed during parsing : {}'. format(certificate_file, e)) return decoded else: return data
def ComparisonValidator(if_value): """Validates and returns the comparator and value.""" if if_value.lower() == 'absent': return (None, None) if len(if_value) < 2: raise exceptions.BadArgumentException('--if', 'Invalid value for flag.') comparator_part = if_value[0] threshold_part = if_value[1:] try: comparator = COMPARISON_TO_ENUM[comparator_part] threshold_value = float(threshold_part) # currently only < and > are supported if comparator not in ['COMPARISON_LT', 'COMPARISON_GT']: raise exceptions.BadArgumentException('--if', 'Comparator must be < or >.') return comparator, threshold_value except KeyError: raise exceptions.BadArgumentException('--if', 'Comparator must be < or >.') except ValueError: raise exceptions.BadArgumentException('--if', 'Threshold not a value float.')
def _BuildExplanationSpec(self, args): """Generate explanation configs if anything related to XAI is specified. Args: args: argparse.Namespace. All the arguments that were provided to this command invocation. Returns: An object of GoogleCloudAiplatformV1ExplanationSpec. Raises: BadArgumentException: An error if the explanation method provided can not be recognized. """ parameters = None method = args.explanation_method if not method: return None if method.lower() == 'integrated-gradients': parameters = ( self.messages.GoogleCloudAiplatformV1ExplanationParameters( integratedGradientsAttribution=self.messages .GoogleCloudAiplatformV1IntegratedGradientsAttribution( stepCount=args.explanation_step_count, smoothGradConfig=self._BuildSmoothGradConfig(args)))) elif method.lower() == 'xrai': parameters = ( self.messages.GoogleCloudAiplatformV1ExplanationParameters( xraiAttribution=self.messages .GoogleCloudAiplatformV1XraiAttribution( stepCount=args.explanation_step_count, smoothGradConfig=self._BuildSmoothGradConfig(args)))) elif method.lower() == 'sampled-shapley': parameters = ( self.messages.GoogleCloudAiplatformV1ExplanationParameters( sampledShapleyAttribution=self.messages .GoogleCloudAiplatformV1SampledShapleyAttribution( pathCount=args.explanation_path_count))) else: raise gcloud_exceptions.BadArgumentException( '--explanation-method', 'Explanation method must be one of `integrated-gradients`, ' '`xrai` and `sampled-shapley`.') return self.messages.GoogleCloudAiplatformV1ExplanationSpec( metadata=self._ReadExplanationMetadata(args.explanation_metadata_file), parameters=parameters)
def SetSource(args, workflow, updated_fields): """Set source for the workflow based on the arguments. Also update updated_fields accordingly. Currently only local source file is supported. Args: args: args passed to the command. workflow: the workflow in which to set the source configuration. updated_fields: a list to which an appropriate source field will be added. """ if args.source: try: workflow.sourceContents = files.ReadFileContents(args.source) except files.MissingFileError: raise exceptions.BadArgumentException( '--source', 'specified file does not exist.') updated_fields.append('sourceContents')
def ValidatePlatformIsManaged(unused_ref, unused_args, req): """Validate the specified platform is managed. This method is referenced by the declaritive iam commands which only work against the managed platform. Args: unused_ref: ref to the service. unused_args: Namespace, The args namespace. req: The request to be made. Returns: Unmodified request """ if GetPlatform() != PLATFORM_MANAGED: raise calliope_exceptions.BadArgumentException( '--platform', 'The platform [{platform}] is not supported by this ' 'operation. Specify `--platform {managed}` or run ' '`gcloud config set run/platform {managed}`.'.format( platform=GetPlatform(), managed=PLATFORM_MANAGED)) return req
def _ReadExplanationMetadata(self, explanation_metadata_file): """Read local explanation metadadta file provided. Args: explanation_metadata_file: str. A local file for explanation metadata. Returns: An object of GoogleCloudAiplatformV1ExplanationMetadata. Raises: BadArgumentException: An error if explanation_metadata_file is None. """ explanation_metadata = None if not explanation_metadata_file: raise gcloud_exceptions.BadArgumentException( '--explanation-metadata-file', 'Explanation metadata file must be specified.') # Yaml is a superset of json, so parse json file as yaml. data = yaml.load_path(explanation_metadata_file) if data: explanation_metadata = messages_util.DictToMessageWithErrorCheck( data, self.messages.GoogleCloudAiplatformV1ExplanationMetadata) return explanation_metadata
def _CreateAgentRules(agent_rules): """Create agent rules in ops agent policy. Args: agent_rules: json objects. Returns: agent rules in ops agent policy. """ ops_agent_rules = [] for agent_rule in agent_rules or []: try: ops_agent_rules.append( agent_policy.OpsAgentPolicy.AgentRule( agent_rule['type'], agent_rule['version'], agent_rule['packageState'], agent_rule['enableAutoupgrade'])) except KeyError as e: raise exceptions.BadArgumentException( 'description.agentRules', 'agent rule specification %s missing a required key: %s' % ( agent_rule, e)) return ops_agent_rules
def _BuildSmoothGradConfig(self, args): if (args.smooth_grad_noise_sigma is None and args.smooth_grad_noisy_sample_count is None and args.smooth_grad_noise_sigma_by_feature is None): return None if (args.smooth_grad_noise_sigma is not None and args.smooth_grad_noise_sigma_by_feature is not None): raise gcloud_exceptions.BadArgumentException( '--smooth-grad-noise-sigma', 'Only one of smooth-grad-noise-sigma ' 'and smooth-grad-noise-sigma-by-feature can be set.') smooth_grad_config = ( self.messages.GoogleCloudAiplatformV1beta1SmoothGradConfig( noiseSigma=args.smooth_grad_noise_sigma, noisySampleCount=args.smooth_grad_noisy_sample_count)) sigmas = args.smooth_grad_noise_sigma_by_feature if sigmas: smooth_grad_config.featureNoiseSigma = ( self.messages .GoogleCloudAiplatformV1beta1FeatureNoiseSigma(noiseSigma=[ self.messages. GoogleCloudAiplatformV1beta1FeatureNoiseSigmaNoiseSigmaForFeature( name=k, sigma=float(sigmas[k])) for k in sigmas ])) return smooth_grad_config