Symantec's domain quota policy enforcement

Change-Id: I1faac8ad9423058949d2c7a0184cdebdd92fecb1
This commit is contained in:
su_zhang 2015-09-17 14:20:30 -07:00
parent 48a0e22292
commit e13f5f2214
3 changed files with 196 additions and 0 deletions

22
contrib/nova/README Normal file
View File

@ -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

154
contrib/nova/congress.py Normal file
View File

@ -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

View File

@ -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)'