Ejemplo n.º 1
0
def jmes_path(source_data: celtypes.Value,
              path_source: celtypes.StringType) -> celtypes.Value:
    """
    Apply JMESPath to an object read from from a URL.
    """
    expression = jmespath.compile(path_source)
    return json_to_cel(expression.search(source_data))
Ejemplo n.º 2
0
def jmes_path_map(source_data: celtypes.ListType,
                  path_source: celtypes.StringType) -> celtypes.ListType:
    """
    Apply JMESPath to a each object read from from a URL.
    This is for ndjson, nljson and jsonl files.
    """
    expression = jmespath.compile(path_source)
    return celtypes.ListType(
        [json_to_cel(expression.search(row)) for row in source_data])
Ejemplo n.º 3
0
def get_metrics(resource: celtypes.MapType,
                request: celtypes.MapType) -> celtypes.Value:
    """
    Reach into C7N and make a statistics request using the current C7N filter.

    This uses the module-global ``C7N`` namespace to access the original filter and policy.

    This builds a request object that is passed through to AWS via the :func:`get_raw_metrics`
    function.

    The ``request`` parameter is a Mapping with the following keys and values:

    ::

        Resource.get_metrics({"MetricName": "CPUUtilization", "Statistic": "Average",
            "StartTime": Now - duration("4d"), "EndTime": Now, "Period": duration("86400s")}
            ).exists(m, m < 30)

    The namespace is derived from the ``C7N.policy``. The dimensions are derived from
    the ``C7N.fiter.model``.

    ..  todo:: Refactor C7N

        Provide a :py:class:`MetricsAccess` mixin in a :py:class:`CELFilter` class.
        We want to have the metrics processing in the new :py:class:`CELFilter` instance.

    """
    dimension = celtypes.StringType(C7N.filter.manager.get_model().dimension)
    namespace = celtypes.StringType(C7N.filter.manager.resource_type)
    # TODO: Varies by resource/policy type. Each policy's model may have different dimensions.
    dimensions = json_to_cel([{
        'Name': dimension,
        'Value': resource.get(dimension)
    }])
    raw_metrics = cast(
        celtypes.ListType,
        get_raw_metrics(
            celtypes.MapType({
                celtypes.StringType("Namespace"):
                namespace,
                celtypes.StringType("MetricName"):
                request["MetricName"],
                celtypes.StringType("Dimensions"):
                dimensions,
                celtypes.StringType("Statistics"): [request["Statistic"]],
                celtypes.StringType("StartTime"):
                request["StartTime"],
                celtypes.StringType("EndTime"):
                request["EndTime"],
                celtypes.StringType("Period"):
                request["Period"],
            })))
    return celtypes.ListType([
        cast(celtypes.MapType, item).get(request["Statistic"])
        for item in raw_metrics
    ])
Ejemplo n.º 4
0
def flow_logs(resource: celtypes.MapType, ) -> celtypes.Value:
    """
    Reach into C7N and make a get_related() request using the current C7N filter.

    ..  todo:: Refactor C7N

        Provide a separate function to get the flow logs, separate from the
        the filter processing.
    """
    client = C7N.filter.client('ec2')
    logs = client.describe_flow_logs().get('FlowLogs', ())
    m = C7N.filter.manager.get_model()
    resource_map: Dict[str, List[Dict[str, Any]]] = {}
    for fl in logs:
        resource_map.setdefault(fl['ResourceId'], []).append(fl)
    if resource.get(m.id) in resource_map:
        flogs = resource_map[cast(str, resource.get(m.id))]
        return json_to_cel(flogs)
    return json_to_cel([])
Ejemplo n.º 5
0
def get_related_ids(resource: celtypes.MapType, ) -> celtypes.Value:
    """
    Reach into C7N and make a get_related_ids() request using the current C7N filter.

    ..  todo:: Refactor C7N

        Provide the :py:class:`RelatedResourceFilter` mixin in a :py:class:`CELFilter` class.
        We want to have the related id's details in the new :py:class:`CELFilter` instance.
    """

    # Assuming the :py:class:`CELFilter` class has this method extracted from the legacy filter.
    security_group_ids = C7N.filter.get_related_ids(resource)
    return json_to_cel(security_group_ids)
Ejemplo n.º 6
0
def subnet(subnet_id: celtypes.Value, ) -> celtypes.Value:
    """
    Reach into C7N and make a get_related() request using the current C7N filter to get
    the subnet.

    ..  todo:: Refactor C7N

        Provide the :py:class:`RelatedResourceFilter` mixin in a :py:class:`CELFilter` class.
        We want to have the related id's details in the new :py:class:`CELFilter` instance.
        See :py:class:`VpcSubnetFilter` subclass of :py:class:`RelatedResourceFilter`.
    """
    # Get related ID's first, then get items for the related ID's.
    subnets = C7N.filter.get_related([subnet_id])
    return json_to_cel(subnets)
Ejemplo n.º 7
0
def security_group(security_group_id: celtypes.Value, ) -> celtypes.Value:
    """
    Reach into C7N and make a get_related() request using the current C7N filter to get
    the security group.

    ..  todo:: Refactor C7N

        Provide the :py:class:`RelatedResourceFilter` mixin in a :py:class:`CELFilter` class.
        We want to have the related id's details in the new :py:class:`CELFilter` instance.
        See :py:class:`VpcSecurityGroupFilter` subclass of :py:class:`RelatedResourceFilter`.
    """

    # Assuming the :py:class:`CELFilter` class has this method extracted from the legacy filter.
    security_groups = C7N.filter.get_related([security_group_id])
    return json_to_cel(security_groups)
Ejemplo n.º 8
0
def parse_text(source_text: celtypes.StringType,
               format: celtypes.StringType) -> celtypes.Value:
    """
    Parse raw text using a given format.
    """
    if format == "json":
        return json_to_cel(json.loads(source_text))
    elif format == "txt":
        return celtypes.ListType([
            celtypes.StringType(s.rstrip()) for s in source_text.splitlines()
        ])
    elif format in ("ldjson", "ndjson", "jsonl"):
        return celtypes.ListType(
            [json_to_cel(json.loads(s)) for s in source_text.splitlines()])
    elif format == "csv":
        return celtypes.ListType(
            [json_to_cel(row) for row in csv.reader(io.StringIO(source_text))])
    elif format == "csv2dict":
        return celtypes.ListType([
            json_to_cel(row)
            for row in csv.DictReader(io.StringIO(source_text))
        ])
    else:
        raise ValueError(f"Unsupported format: {format!r}")  # pragma: no cover
Ejemplo n.º 9
0
def get_raw_metrics(request: celtypes.MapType) -> celtypes.Value:
    """
    Reach into C7N and make a statistics request using the current C7N filter object.

    This uses the module-global ``C7N`` namespace to access the original filter's manager.

    The ``request`` parameter is the request object that is passed through to AWS via
    the current C7N filter's manager. The request is a Mapping with the following keys and values:

    ::

        get_raw_metrics({
            "Namespace": "AWS/EC2",
            "MetricName": "CPUUtilization",
            "Dimensions": {"Name": "InstanceId", "Value": Resource.InstanceId},
            "Statistics": ["Average"],
            "StartTime": Now - duration("4d"),
            "EndTime": Now,
            "Period": duration("86400s")
        })

    The request is passed through to AWS more-or-less directly. The result is a CEL
    list of values for then requested statistic. A ``.map()`` macro
    can be used to compute additional details. An ``.exists()`` macro can filter the
    data to look for actionable values.

    Generally, C7N requests in bunches of 50 per client connection.
    A worker pool processes the batches to keep from overwhelming AWS with
    metrics requests.

    See :py:class:`c7n.filters.metrics.MetricsFilter`. This filter collects
    metrics and applies the filter decision to items in each batch.
    The :py:meth:`process` and :py:meth:`process_resource_set` methods
    need to be refactored into several pieces:

    -   :py:meth:`process_resource_set`. This is the existing interface.
        This calls :py:meth:`prepare_query` to create the various query
        parameters.  It then creates a worker pool and applies :py:meth:`process_resource_set`
        to chunks of 50 resources.

    -   :py:meth:`prepare_query`. This is new. It prepares the parameters
        for :py:meth:`client.get_metric_statistics`.

    -   :py:meth:`process_resource_set`. This is the existing interface.
        It gets a client and then calls :py:meth:`get_resource_statistics` with the client
        and each resource. It calls :py:meth:`filter_resource_statistics` on the results
        of :py:meth:`client.get_metric_statistics`.

    -   :py:meth:`get_resource_statistics`. Given a client and a resource,
        this function will set the resource's ``"c7n.metrics"`` attribute with current
        statistics. This is the ``['Datapoints']`` value. It returns the [self.statistics]
        item from each dictionary in the metrics list of dictionaries.

    -   :py:meth:`filter_resource_statistics`. Given a resource, this function will apply the
        missing-value, the percent-attr and attr-multiplier transformations to the
        resource's ``"c7n.metrics"``.
        It will apply the filter op and value. All of these things better represented in CEL.

    We need to be able to use code something like this:

    ::

        C7N.filter.prepare_query(C7N.policy.resources)
        data = C7N.filter.get_resource_statistics(client, resource)
        return json_to_cel(data)

    ..  todo:: Refactor C7N

        Provide a :py:class:`MetricsAccess` mixin in a :py:class:`CELFilter` class.
        We want to have the metrics processing in the new :py:class:`CELFilter` instance.

    """
    # The preferred design reaches into the policy for an access strategy to get details.
    # data = C7N.filter.get_resource_statistics(
    #     Namespace = request["Namespace"],
    #     MetricName = request["MetricName"],
    #     Statistics = [request["Statistics"]],
    #     StartTime = request["StartTime"],
    #     EndTime = request["EndTime"],
    #     Period = request["Period"],
    #     Dimensions = request["Dimensions"],
    # )

    # An alternative design which acquires data outside the filter object's cache
    client = C7N.filter.manager.session_factory().client('cloudwatch')
    print(f"Client {client}")
    data = client.get_metric_statistics(
        Namespace=request["Namespace"],
        MetricName=request["MetricName"],
        Statistics=[request["Statistics"]],
        StartTime=request["StartTime"],
        EndTime=request["EndTime"],
        Period=request["Period"],
        Dimensions=request["Dimensions"],
    )['Datapoints']
    print(f"data {data}")

    return json_to_cel(data)