Exemple #1
0
    def test_optional_parameters(self):
        callback = self._get_usage_for_project
        enforcer = limit.Enforcer(self.claim, callback=callback, verify=True)

        self.assertEqual(self.claim, enforcer.claim)
        self.assertEqual(self._get_usage_for_project, enforcer.callback)
        self.assertTrue(enforcer.verify)
Exemple #2
0
def enforce_api_limit(entity_type: str, count: int) -> None:
    """Check if the values given are over the limit for that key.

    This is generally used for limiting the size of certain API requests
    that eventually get stored in the database.
    """
    if not nova_limit_utils.use_unified_limits():
        return

    if entity_type not in API_LIMITS:
        fmt = "%s is not a valid API limit: %s"
        raise ValueError(fmt % (entity_type, API_LIMITS))

    try:
        enforcer = limit.Enforcer(always_zero_usage)
    except limit_exceptions.SessionInitError as e:
        msg = (
            "Failed to connect to keystone while enforcing %s quota limit." %
            entity_type)
        LOG.error(msg + " Error: " + str(e))
        raise exception.KeystoneConnectionFailed(msg)

    try:
        enforcer.enforce(None, {entity_type: count})
    except limit_exceptions.ProjectOverLimit as e:
        # Copy the exception message to a OverQuota to propagate to the
        # API layer.
        raise EXCEPTIONS.get(entity_type, exception.OverQuota)(str(e))
Exemple #3
0
def _get_enforcer(context: 'nova.context.RequestContext',
                  project_id: str) -> limit.Enforcer:
    # NOTE(johngarbutt) should we move context arg into oslo.limit?
    def callback(project_id, resource_names):
        return _get_usage(context, project_id, resource_names)

    return limit.Enforcer(callback)
Exemple #4
0
    def test_enforce(self, mock_enforce):
        enforcer = limit.Enforcer(self._get_usage_for_project)
        project_id = uuid.uuid4().hex
        deltas = {"a": 1}

        enforcer.enforce(project_id, deltas)

        mock_enforce.assert_called_once_with(project_id, deltas)
Exemple #5
0
    def test_deltas_must_be_a_dictionary(self):
        project_id = uuid.uuid4().hex
        invalid_delta_types = [uuid.uuid4().hex, 5, 5.1, True, [], None, {}]
        enforcer = limit.Enforcer(self._get_usage_for_project)

        for invalid_delta in invalid_delta_types:
            self.assertRaises(ValueError, enforcer.enforce, project_id,
                              invalid_delta)
Exemple #6
0
    def test_get_model_impl(self):
        json = mock.MagicMock()
        limit._SDK_CONNECTION.get.return_value = json

        json.json.return_value = {"model": {"name": "flat"}}
        enforcer = limit.Enforcer(self._get_usage_for_project)
        flat_impl = enforcer._get_model_impl(self._get_usage_for_project)
        self.assertIsInstance(flat_impl, limit._FlatEnforcer)

        json.json.return_value = {"model": {"name": "strict-two-level"}}
        flat_impl = enforcer._get_model_impl(self._get_usage_for_project)
        self.assertIsInstance(flat_impl, limit._StrictTwoLevelEnforcer)

        json.json.return_value = {"model": {"name": "foo"}}
        e = self.assertRaises(ValueError, enforcer._get_model_impl,
                              self._get_usage_for_project)
        self.assertEqual("enforcement model foo is not supported", str(e))
Exemple #7
0
def enforce_db_limit(context: 'nova.context.RequestContext', entity_type: str,
                     entity_scope: ty.Any, delta: int) -> None:
    """Check provided delta does not put resource over limit.

    Firstly we count the current usage given the specified scope.
    We then add that count to the specified  delta to see if we
    are over the limit for that kind of entity.

    Note previously we used to recheck these limits.
    However these are really soft DDoS protections,
    not hard resource limits, so we don't do the recheck for these.

    The scope is specific to the limit type:
    * key_pairs scope is context.user_id
    * server_groups scope is context.project_id
    * server_group_members scope is server_group_uuid
    """
    if not nova_limit_utils.use_unified_limits():
        return

    if entity_type not in DB_COUNT_FUNCTION.keys():
        fmt = "%s does not have a DB count function defined: %s"
        raise ValueError(fmt % (entity_type, DB_COUNT_FUNCTION.keys()))
    if delta < 0:
        raise ValueError("delta must be a positive integer")

    count_function = DB_COUNT_FUNCTION[entity_type]

    try:
        enforcer = limit.Enforcer(
            functools.partial(count_function, context, entity_scope))
    except limit_exceptions.SessionInitError as e:
        msg = (
            "Failed to connect to keystone while enforcing %s quota limit." %
            entity_type)
        LOG.error(msg + " Error: " + str(e))
        raise exception.KeystoneConnectionFailed(msg)

    try:
        enforcer.enforce(None, {entity_type: delta})
    except limit_exceptions.ProjectOverLimit as e:
        # Copy the exception message to a OverQuota to propagate to the
        # API layer.
        raise EXCEPTIONS.get(entity_type, exception.OverQuota)(str(e))
Exemple #8
0
def _enforce_some(context, project_id, quota_value_fns, deltas):
    """Helper method to enforce a set of quota values.

    :param context: The RequestContext
    :param project_id: The project_id of the tenant being checked
    :param get_value_fns: A mapping of quota names to functions that will be
                          called with no arguments to return the numerical
                          value representing current usage.
    :param deltas: A mapping of quota names to the amount of resource being
                   requested for each (to be added to the current usage before
                   determining if over-quota).
    :raises: exception.LimitExceeded if the current usage is over the defined
             limit.
    :returns: None if the tenant is not currently over their quota.
    """
    if not CONF.use_keystone_limits:
        return

    def callback(project_id, resource_names):
        return {name: quota_value_fns[name]() for name in resource_names}

    enforcer = limit.Enforcer(callback)
    try:
        enforcer.enforce(
            project_id, {
                quota_name: deltas.get(quota_name, 0)
                for quota_name in quota_value_fns
            })
    except ol_exc.ProjectOverLimit as e:
        raise exception.LimitExceeded(body=str(e))
    except ol_exc.SessionInitError as e:
        LOG.error(
            _LE('Failed to initialize oslo_limit, likely due to '
                'incorrect or insufficient configuration: %(err)s'),
            {'err': str(e)})
        # We could just raise LimitExceeded here, but a 500 is
        # appropriate for incorrect server-side configuration, so we
        # re-raise here after the above error message to make sure we
        # are noticed.
        raise
Exemple #9
0
    def do_POST(self):
        data = self.rfile.read(int(self.headers['Content-Length']))
        try:
            admissionRequest = json.loads(data)
        except json.decoder.JSONDecodeError:
            self.send_error(400, "Expected JSON")
            return

        try:
            uid = admissionRequest['request']['uid']
            namespace = admissionRequest['request']['namespace']
        except KeyError:
            self.send_error(400, "Invalid AdmissionReview object")
            return

        kind = admissionRequest['request']['kind']['kind']
        if kind == "Pod":
            requested_pods = 1
        else:
            self.send_error(400, "Only works for pods")
            return

        print("Incoming request: Kind: %s, Pod Count: %d\n" %
              (kind, requested_pods))

        self._setup_openstack_connection()

        sc = self.openstack_conn.config.get_service_catalog()
        endpoint = sc.endpoint_data_for('kubernetes').endpoint_id
        limit.CONF.oslo_limit.endpoint_id = endpoint
        enforcer = limit.Enforcer(self._usage_callback)

        allowed = True

        domain = self._get_parent(namespace)
        if domain:
            try:
                domain = self.openstack_conn.get_domain(
                    name_or_id=domain)['id']
            except openstack.exceptions.ResourceNotFound:
                print("denying request: no matching domain for namespace "
                      "parent")
                allowed = False
            project = self.openstack_conn.get_project(namespace,
                                                      domain_id=domain)
            if project:
                print("found matching project: %s" % project['id'])
            else:
                print("denying request: no matching project or domain")
                allowed = False
        else:
            try:
                project = self.openstack_conn.get_domain(name_or_id=namespace)
                print("found matching domain: %s" % project['id'])
            except openstack.exceptions.ResourceNotFound:
                print("denying request: no matching project or domain")
                allowed = False

        deltas = {'pods': requested_pods}
        if allowed:
            try:
                enforcer.enforce(project['id'], deltas)
                print("Successfully claimed %d pods" % requested_pods)
            except oslo_limit.exception.ProjectOverLimit:
                allowed = False
                print("Could not claim %d pods" % requested_pods)

        admissionResponse = {
            'apiVersion': 'admission.k8s.io/v1',
            'kind': 'AdmissionReview',
            'response': {
                'uid': uid,
                'allowed': allowed
            }
        }
        httpResponse = json.dumps(admissionResponse)
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        self.wfile.write(bytes(httpResponse, 'utf-8'))
Exemple #10
0
def get_legacy_default_limits() -> ty.Dict[str, int]:
    # TODO(johngarbutt): need oslo.limit API for this, it should do caching
    enforcer = limit.Enforcer(lambda: None)
    new_limits = enforcer.get_registered_limits(LEGACY_LIMITS.keys())
    return _convert_keys_to_legacy_name(dict(new_limits))
Exemple #11
0
    def test_required_parameters(self):
        enforcer = limit.Enforcer(self.claim)

        self.assertEqual(self.claim, enforcer.claim)
        self.assertIsNone(enforcer.callback)
        self.assertTrue(enforcer.verify)
Exemple #12
0
 def test_set_model_impl(self):
     enforcer = limit.Enforcer(self._get_usage_for_project)
     self.assertIsInstance(enforcer.model, limit._FlatEnforcer)
Exemple #13
0
 def test_project_id_must_be_a_string(self):
     enforcer = limit.Enforcer(self._get_usage_for_project)
     invalid_delta_types = [{}, 5, 5.1, True, False, [], None, ""]
     for invalid_project_id in invalid_delta_types:
         self.assertRaises(ValueError, enforcer.enforce, invalid_project_id,
                           {})
Exemple #14
0
def get_legacy_project_limits(project_id):
    enforcer = limit.Enforcer(lambda: None)
    new_limits = enforcer.get_project_limits(project_id, LEGACY_LIMITS.keys())
    return _convert_keys_to_legacy_name(dict(new_limits))
Exemple #15
0
def get_legacy_default_limits():
    enforcer = limit.Enforcer(lambda: None)
    new_limits = enforcer.get_registered_limits(LEGACY_LIMITS.keys())
    return _convert_keys_to_legacy_name(dict(new_limits))