def do_custom_validation(module_params):
    if module_params["state"] == "detached" and module_params["attachment"]:
        raise errors.ValidationError(
            "Specifying an attachment when state is detached "
            "is not allowed.")

    if module_params.get("id") is None and module_params.get("subnet") is None:
        raise errors.ValidationError(
            "When not specifying id, subnet is required.")
def validate_identification_params(params):
    if params['state'] == 'associated' and not (params['instance'] or
                                                params['network_interface']):
        raise errors.ValidationError(
            "Missing required parameters: 'instance' or 'network_interface'")

    if params['state'] == 'absent' and not params['ip']:
        raise errors.ValidationError(
            "Missing required parameters: 'ip' to release")
def create(ec2, params, check_mode):
    if check_mode:
        return True, None, None

    vpc = ec2_vpc_utils.get_vpc(ec2, params["vpc"])

    payload = dict(
        CidrBlock=params['cidr'],
    )
    if params['availability_zone']:
        ec2_availability_zone_utils.validate_az_id(ec2, params['availability_zone'])
        payload['AvailabilityZoneId'] = params['availability_zone']

    try:
        subnet = vpc.create_subnet(**payload)
    except boto.ClientError as e:
        code = e.response["Error"]["Code"]
        if code == "InvalidSubnet.Range":
            raise errors.ValidationError("Invalid subnet range: the block size must be between "
                                         "a /28 netmask and /16 netmask.")
        elif code == "InvalidSubnet.Conflict":
            raise errors.ValidationError("Invalid subnet range: the specified CIDR block "
                                         "conflicts with that of another subnet in your VPC.")
        elif code == "InvalidParameterValue":
            raise errors.ValidationError(e.response["Error"]["Message"])
        raise

    subnet_waiter = subnet.meta.client.get_waiter('subnet_available')
    subnet_waiter.wait(SubnetIds=[subnet.id])

    # we cannot tag the subnet at the time of creation,
    # so we do it in a separate call
    subnet.create_tags(
        Tags=tag_utils.to_boto3(params['tags'], name=params['name']),
    )
    if subnet.map_public_ip_on_launch != params['auto_assign_ip']:
        subnet.meta.client.modify_subnet_attribute(
            SubnetId=subnet.id,
            MapPublicIpOnLaunch=dict(
                Value=params['auto_assign_ip'],
            ),
        )

    # get the up-to-date representation with correct state and tags
    subnet.reload()

    return True, subnet, dict(
        before=dict(),
        after=ec2_subnet_utils.result_from_resource(subnet),
    )
def ensure_present(ec2, module_params, check_mode):
    """
    :type module_params: dict
    :type check_mode: bool
    :return: (changed, response_object)
    :rtype: typing.Tuple[bool, dict]
    """

    changed = False
    created, sg = _get_or_create(ec2, module_params, check_mode)
    changed |= created

    # a part of param validation that requires remote state
    if not created and module_params["description"] is not None \
            and module_params["description"] != sg.description:
        raise errors.ValidationError(
            "Changing a security group's description is not supported.")
    if created and check_mode:
        # in this case, sg is a direct dict return as we can't continue changing
        return changed, sg

    updated, sg = _update(sg, module_params, check_mode)
    changed |= updated

    sg.reload()
    return changed, sg.meta.data
def do_new_attachment(ec2, eni, attachment_after):
    instance = ec2_instance_utils.get_instance_by_id(
        ec2,
        attachment_after.instance_id,
        fail_nonexisting_id=True,
        fail_terminated=True)
    if attachment_after.device_index is None:
        device_index = get_first_available_device_index(instance)
    else:
        device_index = attachment_after.device_index

    try:
        id_dict = eni.attach(InstanceId=instance.id, DeviceIndex=device_index)
    except boto.ClientError as e:
        code = e.response["Error"]["Code"]
        if code == "InvalidParameterValue":
            raise errors.ValidationError("Invalid parameter value: {0}".format(
                e.response["Error"]["Message"]))
        elif code == "AttachmentLimitExceeded":
            raise errors.DisallowedOperationError(
                e.response["Error"]["Message"])
        raise
    kot = attachment_after.keep_on_termination
    if kot is None:
        kot = True
    apply_keep_on_termination_policy(eni, id_dict["AttachmentId"], kot)
    eni.load()
Exemple #6
0
def update(vpc, diff, tag_diff, params, check_mode):
    if check_mode:
        return True, None, diff

    vpc_before_update = vpc.meta.data.copy()

    if 'InstanceTenancy' in diff:
        requested_tenancy = diff['InstanceTenancy']['after']
        if requested_tenancy != 'default':
            raise errors.ValidationError(
                "instance_tenancy can only be changed to 'default'")
        vpc.meta.client.modify_vpc_tenancy(
            VpcId=vpc.id,
            InstanceTenancy=requested_tenancy,
        )
    if tag_diff:
        tags_to_update, tags_to_remove = tag_utils.from_diff(
            tag_diff, clear_existing=params['clear_tags'])
        if tags_to_update or tags_to_remove:
            tag_utils.update_resource(vpc, tags_to_update, tags_to_remove)

    vpc.reload()
    return True, vpc, dict(
        before=vpc_before_update,
        after=vpc.meta.data,
    )
def create_new_eni(ec2, subnet, params_entity):
    # the name is a tag, but we still require it for creation and identification
    if params_entity.tags is None or "Name" not in params_entity.tags:
        raise errors.ValidationError(
            "name is required when creating a network interface")

    optional_params = {}
    if params_entity.private_ip_primary:
        optional_params["PrivateIpAddress"] = params_entity.private_ip_primary
        validate_ip_in_cidr(optional_params["PrivateIpAddress"],
                            subnet.cidr_block)
    if params_entity.description:
        optional_params["Description"] = params_entity.description

    if params_entity.security_groups:
        security_groups = params_entity.security_groups
        for sg in security_groups:
            validate_security_group(ec2, sg, subnet.vpc_id)
    else:
        default_sg = ec2_security_group_utils.get_default_security_group_for_vpc(
            ec2, subnet.vpc_id)
        security_groups = [default_sg.id]

    eni = subnet.create_network_interface(
        Groups=security_groups,
        InterfaceType=NetworkInterface.interface_type_internal_to_boto(
            params_entity.eni_type),
        **optional_params)

    waiter = subnet.meta.client.get_waiter("network_interface_available")
    waiter.wait(NetworkInterfaceIds=[eni.id])
    return eni
Exemple #8
0
def create(resource, params, check_mode):
    if check_mode:
        return True, None, None

    payload = dict(CidrBlock=params['cidr'], )

    if params['instance_tenancy']:
        payload['InstanceTenancy'] = params['instance_tenancy']

    try:
        vpc = resource.create_vpc(**payload)
    except boto.ClientError as e:
        # this is documented as InvalidVpcRange, but boto has InvalidVpc.Range
        if e.response["Error"]["Code"] == "InvalidVpc.Range":
            raise errors.ValidationError(
                "Invalid VPC range: The block range must be between a "
                "/28 netmask and /16 netmask.")
        raise
    # we cannot tag the vpc at the time of creation,
    # so we do it in a separate call
    vpc.create_tags(Tags=tag_utils.to_boto3(params['tags'],
                                            name=params['name']), )
    vpc.wait_until_available()

    # get the up-to-date representation with
    # correct state and name tag
    vpc.reload()
    return True, vpc, dict(
        before=dict(),
        after=vpc.meta.data,
    )
def validate_creation_params(params):
    required = {'name', 'cidr'}
    provided = set([p for p in params.keys() if params[p]])
    missing = required - provided
    if missing:
        raise errors.ValidationError(
            "Missing required parameters: '{0}'".format(missing)
        )
def validate_security_group(ec2, secgroup_id, vpc_id):
    if not security_group_exists(ec2, secgroup_id):
        raise errors.ObjectDoesNotExist(
            "Security group does not exist: {0}".format(secgroup_id))

    if not security_group_in_vpc(ec2, secgroup_id, vpc_id):
        raise errors.ValidationError(
            "Security group {0} is not in VPC {1}.".format(
                secgroup_id, vpc_id))
Exemple #11
0
    def validate_prohibited_differing_properties(cls, props):
        """Validates permitted changes between two entities.

        :type props: typing.Iterable[str]
        :raises errors.ValidationError: when a diff validation fails.
        """
        modified_but_prohibited = set(props).intersection(
            cls.properties_modify_prohibited())
        if modified_but_prohibited:
            raise errors.ValidationError(
                "Cannot update property: "
                "{0}".format(", ".join(modified_but_prohibited)))
def validate_eni_detached_or_primary(eni):
    if not eni.attachment:
        return
    if eni.attachment['DeviceIndex'] != 0:
        # we're dealing with a secondary network interface, but
        # we only care for the primary one
        raise errors.ValidationError(
            "Network interface {0} is attached as a secondary "
            "network interface of an existing instance. Provide "
            "a detached network interface when creating an instance, or "
            "a primary network interface to identify an existing instance".
            format(eni.id))
Exemple #13
0
def _service_resource(auth, service_name):
    try:
        resource = _session(auth).resource(service_name,
                                           endpoint_url=_url_or_region_default(
                                               auth.get('url'), service_name,
                                               auth['region']))
    except ValueError as e:
        raise errors.ValidationError("Unable to connect to AWS: {0}".format(
            str(e)))

    verify_auth_success(service_name, resource)
    return resource
Exemple #14
0
def validate_presence(required, params):
    """
    Validates the presence of the required keys in params dict.

    :type required: typing.List[str]
    :type params: dict
    :raises errors.ValidationError: when any of the required keys are
            missing from params.
    """
    provided = [p for p in params.keys() if params[p]]
    missing = set(required) - set(provided)
    if missing:
        raise errors.ValidationError(
            "Missing required parameters: '{0}'".format(", ".join(missing)))
Exemple #15
0
def validate_update(immutable, diff):
    """
    Validates the update by making sure we're not trying to modify an immutable
    property. Param immutable contains the names of immutable properties.
    Param diff contains diff with properties to modify as keys.

    :type immutable: typing.List[str]
    :type diff: dict
    :raises errors.ValidationError: when trying to update immutable properties.
    """
    cannot_update = set(immutable).intersection(diff)
    if cannot_update:
        raise errors.ValidationError(
            'Trying to update immutable properties: {0}'.format(
                ", ".join(cannot_update)))
def validate_identification_params_presence(params):
    # Either one of these suffices for uniquely identifying the instance
    if params['id'] or params['network_interface']:
        return
    # Alternatively, we need any of:
    # a) name, ami, availability_zone
    # b) name, ami, subnet
    required = ['name', 'ami']
    try:
        if not params['subnet']:
            required.append('availability_zone')
        validation.validate_presence(required, params)
    except errors.ValidationError:
        raise errors.ValidationError(
            "You must provide either id or network_interface, "
            "or all of name, ami and [subnet|availability_zone] "
            "to identify the instance")
def _get_or_create(ec2, module_params, check_mode):
    vpc_id = module_params["vpc"]
    changed = False
    if module_params["id"]:
        sg = _get_existing_secgroup_by_id(ec2, module_params["id"])
        if sg is None:
            raise errors.ObjectDoesNotExist(
                "The security group with id '{0}' does not exist.".format(
                    module_params["id"]))
    else:
        # for non-id identification or not-found creation, we default to the default vpc
        if vpc_id is None:
            default_vpc = ec2_vpc_utils.get_default_vpc(ec2)
            vpc_id = default_vpc.id

        sg = _get_existing_secgroup_by_name_and_vpc(ec2, module_params["name"],
                                                    vpc_id)
        if sg is None:
            changed = True

            if module_params["description"] is None:
                raise errors.ValidationError("When creating a security group, "
                                             "a description is required.")

            if check_mode:
                # we stop here, because we don't want to simulate all operations
                # on a virtual security group if we know the result already
                return changed, dict(GroupId="generated-by-aws",
                                     VpcId=vpc_id,
                                     GroupName=module_params["name"],
                                     Description=module_params["description"],
                                     Tags=[],
                                     IpPermissions=[],
                                     IpPermissionsEgress=[
                                         SimpleSecurityGroupRule.
                                         default_egress().to_boto_dict()
                                     ])
            sg = ec2.create_security_group(
                VpcId=vpc_id,
                GroupName=module_params["name"],
                Description=module_params["description"])
            waiter = ec2.meta.client.get_waiter("security_group_exists")
            waiter.wait(GroupIds=[sg.id])
    return changed, sg
def handle_create(ec2, params, check_mode):
    if not params['key_pair']:
        raise errors.ValidationError("key_pair must be set "
                                     "when creating an instance")
    validate_params(ec2, params)

    diff = dict(before={}, )

    subnet = get_subnet(ec2, params["subnet"], params["availability_zone"])

    if check_mode:
        result = ec2_instance_utils.result_from_params(ec2, params, subnet)
        diff['after'] = result
        return True, result, diff

    instance = create(ec2, subnet, params)
    result = ec2_instance_utils.result_from_remote(instance)
    diff['after'] = result

    return True, result, diff
Exemple #19
0
def validate_states(states):
    if 'terminated' in states:
        raise errors.ValidationError(
            "State parameter cannot contain 'terminated'"
        )
def validate_ip_in_cidr(ip, cidr):
    address = ipaddress.ip_address(six.u(ip))
    network = ipaddress.ip_network(six.u(cidr), strict=False)
    if address not in network:
        raise errors.ValidationError("IP {0} not in subnet CIDR {1}.".format(
            ip, cidr))
def verify_detachment_possible(attachment_before):
    if attachment_before.device_index == 0:
        raise errors.ValidationError(
            "Cannot detach interface at device index 0 from instance {0}.".
            format(attachment_before.instance_id))
def validate_identification_params(params):
    if not params['id'] and not params['cidr']:
        raise errors.ValidationError(
            "Missing required parameters: subnet 'id' or 'cidr'"
        )
def validate_tenancy(tenancy, vpc):
    if vpc.instance_tenancy == 'dedicated' and tenancy == 'default':
        raise errors.ValidationError(
            "You cannot launch an instance with default tenancy in "
            "a VPC with dedicated instance tenancy")