def get_subnet_by_cidr_and_vpc(ec2, cidr, vpc_id): # vpc id was not provided, assume default VPC if not vpc_id: default_vpc = ec2_vpc_utils.get_default_vpc(ec2) vpc_id = default_vpc.id subnet_collection = ec2.subnets.filter( Filters=ec2_filters.from_dict({ 'cidr': cidr, 'vpc-id': vpc_id, })) subnets = list(subnet_collection) if not subnets: return None # vpc id + subnet cidr uniquely identify the subnet. # subnet cidr blocks are unique and non-overlapping in the scope of a VPC. # This is why we don't need to handle the case when there is more than # one subnet. subnet = subnets[0] subnet.load() return subnet
def _detach_dependencies(ec2, secgroup_id): """Detaches secgroup from instances as a prerequisite for deletion.""" instances = ec2.instances.filter( Filters=ec2_filters.from_dict({"instance.group-id": secgroup_id})) for inst in instances: existing_groups = inst.security_groups new_groups = [ g for g in existing_groups if g["group-id"] != secgroup_id ] inst.modify_attribute(Groups=new_groups)
def get_default_security_group_for_vpc(ec2, vpc_id): secgroups = list( ec2.security_groups.filter( Filters=ec2_filters.from_dict({ "vpc-id": vpc_id, "group-name": "default" }))) # there is exactly one secgroup that matches this filter, it can't be deleted sg = secgroups[0] sg.load() return sg
def validate_security_group_references(ec2, security_groups, vpc_id): groups = ec2.security_groups.filter( Filters=ec2_filters.from_dict({ 'group-id': security_groups, 'vpc-id': vpc_id, })) present_groups = {group.id for group in groups} invalid_refs = set(security_groups) - present_groups if invalid_refs: raise errors.ObjectDoesNotExist( "Security groups ({0}) do not exist in {1}".format( ", ".join(sorted(invalid_refs)), vpc_id))
def identify_existing_eni(ec2, params_entity): """ Identification logic, in order: - if an ID exists, use (unique = id) - if both subnet and IP exist, use (unique = subnet + ip) - if subnet exists, but IP doesn't exist: - if name and attachment are set, use (subnet + name + attachment (+branch to #1)) - if name is set, but attachment isn't, use (nonunique = subnet + name) - if name is not set, but attachment is set, use (subnet + attachment (+branch to #1)) #1 - if device_index is set, use (unique = instance + device_index) - if device_index is not set, use (nonunique = instance) :type params_entity: NetworkInterface """ if params_entity.eni_id is not None: return get_network_interface_by_id(ec2, params_entity.eni_id, fail_nonexisting_id=True) filters = {} if params_entity.subnet_id is not None: if params_entity.private_ip_primary is not None: filters["subnet-id"] = params_entity.subnet_id filters[ "addresses.private-ip-address"] = params_entity.private_ip_primary filters["addresses.primary"] = "true" else: if params_entity.tags is not None and params_entity.tags.get( "Name") is not None: filters["subnet-id"] = params_entity.subnet_id filters["tag:Name"] = params_entity.tags["Name"] if params_entity.attachment is not None: filters["subnet-id"] = params_entity.subnet_id filters[ "attachment.instance-id"] = params_entity.attachment.instance_id if params_entity.attachment.device_index is not None: filters[ "attachment.device-index"] = params_entity.attachment.device_index if not filters: return None all_enis = list( ec2.network_interfaces.filter(Filters=ec2_filters.from_dict(filters))) if len(all_enis) == 0: return None elif len(all_enis) == 1: all_enis[0].load() return all_enis[0] raise errors.UnexpectedStateError( "More than one network interface exists matching the task's identifying arguments." )
def _create_keypair_new(client, key_name, public_key_material, check_mode): if check_mode: return None, True else: # fingerprints are not important here and have been checked in the action plugin if public_key_material is None: response_object = client.create_key_pair(KeyName=key_name) else: response_object = client.import_key_pair( KeyName=key_name, PublicKeyMaterial=public_key_material) waiter = client.get_waiter("key_pair_exists") waiter.wait(Filters=ec2_filters.from_dict({"key-name": key_name})) return response_object, True
def get_subnet_by_id(ec2, subnet_id, fail_nonexisting_id=False): subnet = ec2.Subnet(subnet_id) if subnet not in ec2.subnets.filter( Filters=ec2_filters.from_dict({ 'subnet-id': subnet_id, })): if fail_nonexisting_id: raise errors.ObjectDoesNotExist( 'Subnet with id {0} does not exist'.format(subnet_id)) return None subnet.load() return subnet
def get_internet_gateway_by_id(ec2, gateway_id, fail_nonexisting_id=False): gateway = ec2.InternetGateway(gateway_id) if gateway not in ec2.internet_gateways.filter( Filters=ec2_filters.from_dict({ 'internet-gateway-id': gateway_id, })): if fail_nonexisting_id: raise errors.ObjectDoesNotExist( 'Internet gateway with id {0} does not exist.'.format( gateway_id)) return None gateway.load() return gateway
def _get_existing_secgroup_by_name_and_vpc(ec2, name, vpc_id): # errors out when this does not exist # prevents boto.ClientError.response["Error"]["Code"] == "InvalidParameterValue" below vpc = ec2_vpc_utils.get_vpc(ec2, vpc_id) secgroups = list( vpc.security_groups.filter( Filters=ec2_filters.from_dict({"group-name": name}))) if len(secgroups) == 0: return None elif len(secgroups) == 1: return secgroups[0] raise errors.AmbiguousObjectError( "More than one security group with the name '{0}' " "exists in VPC {1}".format(name, vpc_id))
def _update_keypair(client, key_name, public_key_material, fingerprints, force, check_mode, existing_keypair): response_object = existing_keypair changed = False warning = None if public_key_material is None: if force: changed = True if check_mode: # we don't want to return the initial object, as that'd be incorrect response_object = None else: client.delete_key_pair(KeyName=key_name) response_object = client.create_key_pair(KeyName=key_name) waiter = client.get_waiter("key_pair_exists") waiter.wait( Filters=ec2_filters.from_dict({"key-name": key_name})) else: warning = "Any subsequent invocation of this module does not return the remotely-" \ "generated private key as only the initial generation creates it!" else: # existing_key with same name exists, public_key specified local_fingerprint_matches_existing_keypair = \ any(fp for fp in fingerprints if fingerprints_equal(fp, existing_keypair["KeyFingerprint"])) if local_fingerprint_matches_existing_keypair: # the fingerprint now MUST be MD5, as there is no way to have a local SHA1 fingerprint # do nothing, locally-generated key already present pass else: if force: changed = True if check_mode: # same argumentation as above response_object = None else: client.delete_key_pair(KeyName=key_name) response_object = client.import_key_pair( KeyName=key_name, PublicKeyMaterial=public_key_material) else: raise errors.DisallowedOperationError( "A key pair with name {0} already exists. Set force: true to overwrite." .format(key_name)) return response_object, changed, warning
def _get_vpc_by_primary_cidr_and_name(ec2, name, cidr): vpc_collection = ec2.vpcs.filter( Filters=ec2_filters.from_dict({ 'cidr': cidr, 'tag:Name': name, })) vpcs = list(vpc_collection) # It can happen that we have multiple VPCs with identical CIDR # and name tags on AWS if len(vpcs) > 1: raise errors.AmbiguousObjectError( "Duplicate VPCs exist ({0}), could not reliably " "determine the correct one.".format(vpcs)) if not vpcs: return None return vpcs[0]
def get_default_vpc(ec2): """ Retrieves the default VPC. :raises errors.ObjectDoesNotExist: when the default VPC does not exist. """ vpc_collection = ec2.vpcs.filter( Filters=ec2_filters.from_dict(dict(isDefault='true', ))) vpcs = list(vpc_collection) if not vpcs: # we don't have a default VPC raise errors.ObjectDoesNotExist( "Default VPC does not exist. Create one or pass " "the VPC id via 'vpc' parameter.") # we cannot have more than one default VPC per region vpc = vpcs[0] vpc.load() return vpc
def get_internet_gateway_by_name(ec2, gateway_name): gateway_collection = ec2.internet_gateways.filter( Filters=ec2_filters.from_dict({ 'tag:Name': gateway_name, })) gateways = list(gateway_collection) if not gateways: return None if len(gateways) > 1: raise errors.AmbiguousObjectError( "More than one internet gateway named {0} exists".format( gateway_name)) gateway = gateways[0] gateway.load() return gateway
def get_default_subnet_for_az(ec2, availability_zone_id): subnet_collection = ec2.subnets.filter( Filters=ec2_filters.from_dict({ 'availability-zone-id': availability_zone_id, 'default-for-az': 'true', })) subnets = list(subnet_collection) if not subnets: raise errors.ObjectDoesNotExist( 'Availability zone with id {0} does not' ' have a default subnet'.format(availability_zone_id)) # there can't be more than 1 default subnet per AZ subnet = subnets[0] subnet.load() return subnet
def update_monitoring(instance, monitoring_type, check_mode): # Reference: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-cloudwatch-new.html # noqa: 501 validate_state_dependent_operation(instance, ['running', 'stopped'], 'CloudWatch monitoring') if check_mode: return if monitoring_type == 'detailed': instance.monitor() wait_state = 'enabled' else: instance.unmonitor() wait_state = 'disabled' # modifying monitoring state is an async operation, # so we need to wait to ensure the desired state instance.wait_until_exists( Filters=ec2_filters.from_dict({ 'monitoring-state': wait_state, }))
def get_instance_by_alternative_params(ec2, name, ami, subnet_id): instance_collection = ec2.instances.filter(Filters=ec2_filters.from_dict( { 'tag:Name': name, 'image-id': ami, 'subnet-id': subnet_id, 'instance-state-name': list(ec2_instance_utils.PRESENT_STATES), })) instances = list(instance_collection) if not instances: return None if len(instances) > 1: raise errors.AmbiguousObjectError( "Unable to identify the instance based on name ({0})," "ami ({1}) and subnet ({2})".format(name, ami, subnet_id)) instance = instances[0] instance.load() return instance
def detach_and_wait(eni): eni.detach() waiter = eni.meta.client.get_waiter("network_interface_available") waiter.wait(NetworkInterfaceIds=[eni.id], Filters=ec2_filters.from_dict({"status": "available"}))