summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/compute/contrib/coverage_ext.py236
1 files changed, 236 insertions, 0 deletions
diff --git a/nova/api/openstack/compute/contrib/coverage_ext.py b/nova/api/openstack/compute/contrib/coverage_ext.py
new file mode 100644
index 000000000..954eddf4c
--- /dev/null
+++ b/nova/api/openstack/compute/contrib/coverage_ext.py
@@ -0,0 +1,236 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 IBM
+#
+# 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
+
+# See: http://wiki.openstack.org/Nova/CoverageExtension for more information
+# and usage explanation for this API extension
+
+import os
+import re
+import sys
+import telnetlib
+import tempfile
+import time
+
+from coverage import coverage
+from webob import exc
+
+from nova.api.openstack import common
+from nova.api.openstack import extensions
+from nova.api.openstack import wsgi
+from nova.compute import api as compute_api
+from nova import db
+from nova.network import api as network_api
+from nova.openstack.common import log as logging
+from nova import utils
+
+
+LOG = logging.getLogger(__name__)
+authorize = extensions.extension_authorizer('compute', 'coverage_ext')
+
+
+class CoverageController(object):
+ """The Coverage report API controller for the OpenStack API"""
+ def __init__(self):
+ self.data_path = tempfile.mkdtemp(prefix='nova-coverage_')
+ data_out = os.path.join(self.data_path, '.nova-coverage')
+ self.coverInst = coverage(data_file=data_out)
+ self.compute_api = compute_api.API()
+ self.network_api = network_api.API()
+ self.services = []
+ self.combine = False
+ super(CoverageController, self).__init__()
+
+ def _find_services(self, req):
+ """Returns a list of services"""
+ context = req.environ['nova.context']
+ services = db.service_get_all(context, False)
+ hosts = []
+ for serv in services:
+ hosts.append({"service": serv["topic"], "host": serv["host"]})
+ return hosts
+
+ def _find_ports(self, req, hosts):
+ """Return a list of backdoor ports for all services in the list"""
+ context = req.environ['nova.context']
+
+ apicommands = {
+ "compute": self.compute_api.get_backdoor_port,
+ "network": self.network_api.get_backdoor_port,
+ }
+ ports = []
+ temp = {}
+ #TODO(mtreinish): Figure out how to bind the backdoor socket to 0.0.0.0
+ # Currently this will only work if the host is resolved as loopback on
+ # the same host as api-server
+ for host in hosts:
+ if host['service'] in apicommands:
+ get_port_fn = apicommands[host['service']]
+ _host = host
+ _host['port'] = get_port_fn(context, host['host'])
+ ports.append(_host)
+ else:
+ LOG.debug(_("No backdoor API command for service: %s\n"), host)
+ return ports
+
+ def _start_coverage_telnet(self, tn, service):
+ tn.write('import sys\n')
+ tn.write('from coverage import coverage\n')
+ if self.combine:
+ data_file = os.path.join(self.data_path,
+ '.nova-coverage.%s' % str(service))
+ tn.write("coverInst = coverage(data_file='%s')\n)" % data_file)
+ else:
+ tn.write('coverInst = coverage()\n')
+ tn.write('coverInst.skipModules = sys.modules.keys()\n')
+ tn.write("coverInst.start()\n")
+ tn.write("print 'finished'\n")
+ tn.expect([re.compile('finished')])
+
+ def _start_coverage(self, req, body):
+ '''Begin recording coverage information.'''
+ LOG.debug("Coverage begin")
+ body = body['start']
+ self.combine = False
+ if 'combine' in body.keys():
+ self.combine = bool(body['combine'])
+ self.coverInst.skipModules = sys.modules.keys()
+ self.coverInst.start()
+ hosts = self._find_services(req)
+ ports = self._find_ports(req, hosts)
+ self.services = []
+ for service in ports:
+ service['telnet'] = telnetlib.Telnet(service['host'],
+ service['port'])
+ self.services.append(service)
+ self._start_coverage_telnet(service['telnet'], service['service'])
+
+ def _stop_coverage_telnet(self, tn):
+ tn.write("coverInst.stop()\n")
+ tn.write("coverInst.save()\n")
+ tn.write("print 'finished'\n")
+ tn.expect([re.compile('finished')])
+
+ def _check_coverage(self):
+ try:
+ self.coverInst.stop()
+ self.coverInst.save()
+ except AssertionError:
+ return True
+ return False
+
+ def _stop_coverage(self, req):
+ for service in self.services:
+ self._stop_coverage_telnet(service['telnet'])
+ if self._check_coverage():
+ msg = ("Coverage not running")
+ raise exc.HTTPNotFound(explanation=msg)
+
+ def _report_coverage_telnet(self, tn, path, xml=False):
+ if xml:
+ execute = str("coverInst.xml_report(outfile='%s')\n" % path)
+ tn.write(execute)
+ tn.write("print 'finished'\n")
+ tn.expect([re.compile('finished')])
+ else:
+ execute = str("output = open('%s', 'w')\n" % path)
+ tn.write(execute)
+ tn.write("coverInst.report(file=output)\n")
+ tn.write("output.close()\n")
+ tn.write("print 'finished'\n")
+ tn.expect([re.compile('finished')])
+ tn.close()
+
+ def _report_coverage(self, req, body):
+ self._stop_coverage(req)
+ xml = False
+ path = None
+
+ body = body['report']
+ if 'file' in body.keys():
+ path = body['file']
+ if path != os.path.basename(path):
+ msg = ("Invalid path")
+ raise exc.HTTPBadRequest(explanation=msg)
+ path = os.path.join(self.data_path, path)
+ else:
+ msg = ("No path given for report file")
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ if 'xml' in body.keys():
+ xml = body['xml']
+
+ if self.combine:
+ self.coverInst.combine()
+ if xml:
+ self.coverInst.xml_report(outfile=path)
+ else:
+ output = open(path, 'w')
+ self.coverInst.report(file=output)
+ output.close()
+ for service in self.services:
+ service['telnet'].close()
+ else:
+ if xml:
+ apipath = path + '.api'
+ self.coverInst.xml_report(outfile=apipath)
+ for service in self.services:
+ self._report_coverage_telnet(service['telnet'],
+ path + '.%s'
+ % service['service'],
+ xml=True)
+ else:
+ output = open(path + '.api', 'w')
+ self.coverInst.report(file=output)
+ for service in self.services:
+ self._report_coverage_telnet(service['telnet'],
+ path + '.%s' % service['service'])
+ output.close()
+ return {'path': path}
+
+ def action(self, req, body):
+ _actions = {
+ 'start': self._start_coverage,
+ 'stop': self._stop_coverage,
+ 'report': self._report_coverage,
+ }
+ authorize(req.environ['nova.context'])
+ for action, data in body.iteritems():
+ if action == 'stop':
+ return _actions[action](req)
+ elif action == 'report' or action == 'start':
+ return _actions[action](req, body)
+ else:
+ msg = _("Coverage doesn't have %s action") % action
+ raise exc.HTTPBadRequest(explanation=msg)
+ raise exc.HTTPBadRequest(explanation=_("Invalid request body"))
+
+
+class Coverage_ext(extensions.ExtensionDescriptor):
+ """Enable Nova Coverage"""
+
+ name = "Coverage"
+ alias = "os-coverage"
+ namespace = ("http://docs.openstack.org/compute/ext/"
+ "coverage/api/v2")
+ updated = "2012-10-15T00:00:00+00:00"
+
+ def get_resources(self):
+ resources = []
+ res = extensions.ResourceExtension('os-coverage',
+ controller=CoverageController(),
+ collection_actions={"action": "POST"})
+ resources.append(res)
+ return resources