Symantec's domain quota policy enforcement
Change-Id: I1faac8ad9423058949d2c7a0184cdebdd92fecb1
This commit is contained in:
parent
48a0e22292
commit
e13f5f2214
|
@ -0,0 +1,22 @@
|
|||
|
||||
Steps to install this policy extension:
|
||||
1. Install congress client on nova controllers
|
||||
2. Install (copy) policy module (congress.py) under nova/api
|
||||
3. In api-paste.ini, declare congress filter and insert it to the execution flow.
|
||||
Congress filter usually sits in between the final application 'osapi_compute_app_v2' and keystone context.
|
||||
For example, congress filter can be declared as
|
||||
"
|
||||
[filter:congress]
|
||||
paste.filter_factory = nova.api.congress:Congress.factory
|
||||
"
|
||||
And insert congress into the execution flow like
|
||||
"
|
||||
keystone_nolimit = compute_req_id faultwrap sizelimit authtoken keystonecontext congress osapi_compute_app_v2
|
||||
"
|
||||
4. Push policies into congress server as indicated in "sample_policies"
|
||||
|
||||
P.S. This policy enforcement is supposed to go with admin credentials in order to have a whole picture at domain level.
|
||||
Therefore we turn on "all_tenent" option while looking up resource usage across the entire domain.
|
||||
|
||||
|
||||
More info can be found within this public slides: https://drive.google.com/file/d/0B5VvD3PSoDPaLTVIWG1NNDhQRFE/view?usp=sharing
|
|
@ -0,0 +1,154 @@
|
|||
# Copyright 2015 Symantec.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Congress Policy Middleware.
|
||||
|
||||
"""
|
||||
import json
|
||||
|
||||
from nova.i18n import _
|
||||
from nova import wsgi
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import webob.dec
|
||||
import webob.exc
|
||||
|
||||
# policy enforcement flow
|
||||
from congressclient.v1 import client
|
||||
import keystoneclient
|
||||
from keystoneclient.v3 import client as ksv3client
|
||||
from novaclient.v2 import client as nova_v2
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Congress(wsgi.Middleware):
|
||||
"""Make a request context from keystone headers."""
|
||||
|
||||
@webob.dec.wsgify(RequestClass=wsgi.Request)
|
||||
def __call__(self, req):
|
||||
|
||||
if req.environ['REQUEST_METHOD'] != 'POST':
|
||||
return self.application
|
||||
|
||||
raw_path = req.environ['RAW_PATH_INFO']
|
||||
|
||||
if "metadata" in raw_path:
|
||||
return self.application
|
||||
|
||||
if "servers/action" in raw_path:
|
||||
return self.application
|
||||
|
||||
flavor_ref = json.loads(req.body)['server']['flavorRef']
|
||||
|
||||
token = req.environ['HTTP_X_AUTH_TOKEN']
|
||||
|
||||
tenant_name = req.environ['HTTP_X_TENANT_NAME']
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
# obtain identity endpoint url
|
||||
url = CONF.keystone_authtoken.auth_url
|
||||
|
||||
# obtain one of support keystone api versions
|
||||
raw_versions = keystoneclient.discover.available_versions(url,
|
||||
session=None)
|
||||
version = raw_versions[-1]['id']
|
||||
|
||||
# assemble auth_url
|
||||
auth_url = url + '/' + version
|
||||
|
||||
auth = keystoneclient.auth.identity.v2.Token(
|
||||
auth_url=auth_url,
|
||||
token=token, tenant_name=tenant_name)
|
||||
|
||||
session = keystoneclient.session.Session(auth=auth)
|
||||
congress = client.Client(session=session,
|
||||
auth=None,
|
||||
interface='publicURL',
|
||||
service_type='policy')
|
||||
|
||||
# Aggregating resource usage within domain level
|
||||
domain = req.environ['HTTP_X_PROJECT_DOMAIN_NAME']
|
||||
|
||||
# obtain list of projects under this domain
|
||||
k3_client = ksv3client.Client(session=session)
|
||||
|
||||
projects = k3_client.projects.list(domain=domain)
|
||||
# obtain list of hosts under each of these projects
|
||||
|
||||
nova_c = nova_v2.Client(session=session)
|
||||
ram_p = 0
|
||||
disk_p = 0
|
||||
cpus_p = 0
|
||||
for project in projects:
|
||||
|
||||
search_opts = {
|
||||
'all_tenants': 1,
|
||||
'tenant_id': project.id,
|
||||
}
|
||||
|
||||
servers_p = nova_c.servers.list(search_opts=search_opts)
|
||||
|
||||
# locate flavor of each host
|
||||
for server in servers_p:
|
||||
|
||||
info = nova_c.servers.get(server=server)
|
||||
flavor_id = info._info['flavor']['id']
|
||||
fd = nova_c.flavors.get(flavor=flavor_id)
|
||||
ram_p += fd.ram
|
||||
disk_p += fd.disk
|
||||
disk_p += fd.ephemeral
|
||||
cpus_p += fd.vcpus
|
||||
|
||||
# incrementally add each type of resource
|
||||
# assemble query policy based on the data-usage
|
||||
# with memory_p, disk_p and cpus_p
|
||||
|
||||
fd = nova_c.flavors.get(flavor=flavor_ref)
|
||||
ram_p += fd.ram
|
||||
disk_p += fd.disk
|
||||
disk_p += fd.ephemeral
|
||||
cpus_p += fd.vcpus
|
||||
domain_resource = ("(" + domain + "," + str(ram_p) + "," +
|
||||
str(disk_p) + "," + str(cpus_p) + ")")
|
||||
|
||||
validation_result = congress.execute_policy_action(
|
||||
"classification",
|
||||
"simulate",
|
||||
False,
|
||||
True,
|
||||
{'query': 'domain_resource_usage_exceeded (domain)',
|
||||
# this needs to be defined in congress server
|
||||
'action_policy': 'nova_quota_action',
|
||||
'sequence': 'domain_resource+'+domain_resource})
|
||||
|
||||
if validation_result["result"]:
|
||||
|
||||
messages = validation_result["result"]
|
||||
|
||||
if messages:
|
||||
result_str = "\n ".join(map(str, messages))
|
||||
msg = _(
|
||||
"quota is not sufficient for this VM deployment").format(
|
||||
"\n " + result_str)
|
||||
LOG.error(msg)
|
||||
|
||||
LOG.debug(messages)
|
||||
return webob.exc.HTTPUnauthorized(explanation=msg)
|
||||
else:
|
||||
LOG.info('Model valid')
|
||||
|
||||
return self.application
|
|
@ -0,0 +1,20 @@
|
|||
Sample policies in Datalog format:
|
||||
|
||||
domain_resource_usage_exceeded (domain):-
|
||||
domain_resource(domain, ram, disk, cpus),
|
||||
gt(ram, 550).
|
||||
|
||||
domain_resource_usage_exceeded (domain):-
|
||||
domain_resource(domain, ram, disk, cpus),
|
||||
gt(disk, 2).
|
||||
|
||||
domain_resource_usage_exceeded (domain):-
|
||||
domain_resource(domain, ram, disk, cpus),
|
||||
gt(cpu, 2).
|
||||
|
||||
|
||||
|
||||
# How to push these policies into congress server
|
||||
openstack congress policy rule create classification 'domain_resource_usage_exceeded (domain):- domain_resource(domain, ram, disk, cpus), gt(cpus, 2)'
|
||||
openstack congress policy rule create classification 'domain_resource_usage_exceeded (domain):- domain_resource(domain, ram, disk, cpus), gt(disk, 2)'
|
||||
openstack congress policy rule create classification 'domain_resource_usage_exceeded (domain):- domain_resource(domain, ram, disk, cpus), gt(ram, 550)'
|
Loading…
Reference in New Issue