diff options
| author | Ed Leafe <ed@leafe.com> | 2011-01-03 09:56:49 -0600 |
|---|---|---|
| committer | Ed Leafe <ed@leafe.com> | 2011-01-03 09:56:49 -0600 |
| commit | 5b240fe5d963e195525c42bf097d79b3f003c8d4 (patch) | |
| tree | 6da1ba783f3d713d5fad37d0ed693e0dc1086a60 | |
| parent | 108352d5c132f6accc79974d8c646a2bc7d4f127 (diff) | |
| parent | 0e88a58cf95bf9298a52d132cd1eb02f29c6bfe1 (diff) | |
merged latest trunk
| -rw-r--r-- | .bzrignore | 1 | ||||
| -rw-r--r-- | nova/api/openstack/__init__.py | 2 | ||||
| -rw-r--r-- | nova/api/openstack/servers.py | 10 | ||||
| -rw-r--r-- | nova/compute/api.py | 14 | ||||
| -rw-r--r-- | nova/compute/manager.py | 9 | ||||
| -rw-r--r-- | nova/db/api.py | 5 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 12 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/models.py | 21 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_servers.py | 16 | ||||
| -rw-r--r-- | nova/tests/test_xenapi.py | 6 | ||||
| -rw-r--r-- | nova/virt/fake.py | 3 | ||||
| -rw-r--r-- | nova/virt/libvirt_conn.py | 3 | ||||
| -rw-r--r-- | nova/virt/xenapi/vm_utils.py | 4 | ||||
| -rw-r--r-- | nova/virt/xenapi/vmops.py | 19 | ||||
| -rw-r--r-- | nova/virt/xenapi_conn.py | 6 | ||||
| -rw-r--r-- | run_tests.py | 68 | ||||
| -rwxr-xr-x | run_tests.sh | 11 |
17 files changed, 168 insertions, 42 deletions
diff --git a/.bzrignore b/.bzrignore index 82db46fa2..d81a7d829 100644 --- a/.bzrignore +++ b/.bzrignore @@ -6,6 +6,7 @@ keys networks nova.sqlite CA/cacert.pem +CA/crl.pem CA/index.txt* CA/openssl.cnf CA/serial* diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index 66aceee2d..ea6dff004 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -93,6 +93,8 @@ class APIRouter(wsgi.Router): logging.debug("Including admin operations in API.") server_members['pause'] = 'POST' server_members['unpause'] = 'POST' + server_members["diagnostics"] = "GET" + server_members["actions"] = "GET" server_members['suspend'] = 'POST' server_members['resume'] = 'POST' diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py index 845183258..c5cbe21ef 100644 --- a/nova/api/openstack/servers.py +++ b/nova/api/openstack/servers.py @@ -218,3 +218,13 @@ class Controller(wsgi.Controller): logging.error(_("compute.api::resume %s"), readable) return faults.Fault(exc.HTTPUnprocessableEntity()) return exc.HTTPAccepted() + + def diagnostics(self, req, id): + """Permit Admins to retrieve server diagnostics.""" + ctxt = req.environ["nova.context"] + return self.compute_api.get_diagnostics(ctxt, id) + + def actions(self, req, id): + """Permit Admins to retrieve server actions.""" + ctxt = req.environ["nova.context"] + return self.compute_api.get_actions(ctxt, id) diff --git a/nova/compute/api.py b/nova/compute/api.py index ef49a4e6e..4a18c3d7d 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -280,6 +280,20 @@ class ComputeAPI(base.Base): """Unpause the given instance.""" self._cast_compute_message("unpause_instance", context, instance_id) + def get_diagnostics(self, context, instance_id): + """Retrieve diagnostics for the given instance.""" + instance = self.db.instance_get_by_internal_id(context, instance_id) + host = instance["host"] + return rpc.call(context, + self.db.queue_get_for(context, FLAGS.compute_topic, host), + {"method": "get_diagnostics", + "args": {"instance_id": instance["id"]}}) + + def get_actions(self, context, instance_id): + """Retrieve actions for the given instance.""" + instance = self.db.instance_get_by_internal_id(context, instance_id) + return self.db.instance_get_actions(context, instance["id"]) + def suspend(self, context, instance_id): """suspend the instance with instance_id""" instance = self.db.instance_get_by_internal_id(context, instance_id) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 583fabe42..2c5ad65ae 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -351,6 +351,15 @@ class ComputeManager(manager.Manager): result)) @exception.wrap_exception + def get_diagnostics(self, context, instance_id): + """Retrieve diagnostics for an instance on this server.""" + instance_ref = self.db.instance_get(context, instance_id) + + if instance_ref["state"] == power_state.RUNNING: + logging.debug(_("instance %s: retrieving diagnostics"), + instance_ref["internal_id"]) + return self.driver.get_diagnostics(instance_ref) + def suspend_instance(self, context, instance_id): """suspend the instance with instance_id""" context = context.elevated() diff --git a/nova/db/api.py b/nova/db/api.py index fde3f0852..127f15478 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -383,6 +383,11 @@ def instance_action_create(context, values): return IMPL.instance_action_create(context, values) +def instance_get_actions(context, instance_id): + """Get instance actions by instance id.""" + return IMPL.instance_get_actions(context, instance_id) + + ################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 7e945e4cb..8e68d12a4 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -856,6 +856,18 @@ def instance_action_create(context, values): return action_ref +@require_admin_context +def instance_get_actions(context, instance_id): + """Return the actions associated to the given instance id""" + session = get_session() + actions = {} + for action in session.query(models.InstanceActions).\ + filter_by(instance_id=instance_id).\ + all(): + actions[action.action] = action.error + return actions + + ################### diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 693db8d23..a050aef23 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -22,7 +22,7 @@ SQLAlchemy models for nova data. import datetime from sqlalchemy.orm import relationship, backref, object_mapper -from sqlalchemy import Column, Integer, Float, String, schema +from sqlalchemy import Column, Integer, String, schema from sqlalchemy import ForeignKey, DateTime, Boolean, Text from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.declarative import declarative_base @@ -236,21 +236,6 @@ class Instance(BASE, NovaBase): # 'shutdown', 'shutoff', 'crashed']) -class InstanceDiagnostics(BASE, NovaBase): - """Represents a guest VM's diagnostics""" - __tablename__ = "instance_diagnostics" - id = Column(Integer, primary_key=True) - instance_id = Column(Integer, ForeignKey('instances.id')) - - memory_available = Column(Float) - memory_free = Column(Float) - cpu_load = Column(Float) - disk_read = Column(Float) - disk_write = Column(Float) - net_tx = Column(Float) - net_rx = Column(Float) - - class InstanceActions(BASE, NovaBase): """Represents a guest VM's actions and results""" __tablename__ = "instance_actions" @@ -452,7 +437,7 @@ class AuthToken(BASE, NovaBase): """ __tablename__ = 'auth_tokens' token_hash = Column(String(255), primary_key=True) - user_id = Column(Integer) + user_id = Column(String(255)) server_manageent_url = Column(String(255)) storage_url = Column(String(255)) cdn_management_url = Column(String(255)) @@ -561,7 +546,7 @@ def register_models(): it will never need to be called explicitly elsewhere. """ from sqlalchemy import create_engine - models = (Service, Instance, InstanceDiagnostics, InstanceActions, + models = (Service, Instance, InstanceActions, Volume, ExportDevice, IscsiTarget, FixedIp, FloatingIp, Network, SecurityGroup, SecurityGroupIngressRule, SecurityGroupInstanceAssociation, AuthToken, User, diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py index 5d23db588..70ff714e6 100644 --- a/nova/tests/api/openstack/test_servers.py +++ b/nova/tests/api/openstack/test_servers.py @@ -95,6 +95,10 @@ class ServersTest(unittest.TestCase): fake_compute_api) self.stubs.Set(nova.compute.api.ComputeAPI, 'resume', fake_compute_api) + self.stubs.Set(nova.compute.api.ComputeAPI, "get_diagnostics", + fake_compute_api) + self.stubs.Set(nova.compute.api.ComputeAPI, "get_actions", + fake_compute_api) self.allow_admin = FLAGS.allow_admin_api def tearDown(self): @@ -274,6 +278,18 @@ class ServersTest(unittest.TestCase): res = req.get_response(nova.api.API('os')) self.assertEqual(res.status_int, 202) + def test_server_diagnostics(self): + req = webob.Request.blank("/v1.0/servers/1/diagnostics") + req.method = "GET" + res = req.get_response(nova.api.API("os")) + self.assertEqual(res.status_int, 404) + + def test_server_actions(self): + req = webob.Request.blank("/v1.0/servers/1/actions") + req.method = "GET" + res = req.get_response(nova.api.API("os")) + self.assertEqual(res.status_int, 404) + def test_server_reboot(self): body = dict(server=dict( name='server_test', imageId=2, flavorId=2, metadata={}, diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index f0d84e9aa..c95a53af3 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -166,6 +166,10 @@ class XenAPIVMTestCase(test.TestCase): instances = self.conn.list_instances() self.assertEquals(instances, []) + def test_get_diagnostics(self): + instance = self._create_instance() + self.conn.get_diagnostics(instance) + def test_instance_snapshot(self): stubs.stubout_instance_snapshot(self.stubs) instance = self._create_instance() @@ -253,7 +257,7 @@ class XenAPIVMTestCase(test.TestCase): 'kernel_id': 2, 'ramdisk_id': 3, 'instance_type': 'm1.large', - 'mac_address': 'aa:bb:cc:dd:ee:ff', + 'mac_address': 'aa:bb:cc:dd:ee:ff' } instance = db.instance_create(values) self.conn.spawn(instance) diff --git a/nova/virt/fake.py b/nova/virt/fake.py index fdc8ac5fb..2b9cf1ca3 100644 --- a/nova/virt/fake.py +++ b/nova/virt/fake.py @@ -228,6 +228,9 @@ class FakeConnection(object): 'num_cpu': 2, 'cpu_time': 0} + def get_diagnostics(self, instance_name): + pass + def list_disks(self, instance_name): """ Return the IDs of all the virtual disks attached to the specified diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 3a9e58b50..00edfbdc8 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -587,6 +587,9 @@ class LibvirtConnection(object): 'num_cpu': num_cpu, 'cpu_time': cpu_time} + def get_diagnostics(self, instance_name): + raise exception.APIError("diagnostics are not supported for libvirt") + def get_disks(self, instance_name): """ Note that this function takes an instance name, not an Instance, so diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 218c147ce..9d1b51848 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -347,6 +347,10 @@ class VMHelper(HelperBase): try: host = session.get_xenapi_host() host_ip = session.get_xenapi().host.get_record(host)["address"] + except (cls.XenAPI.Failure, KeyError) as e: + return {"Unable to retrieve diagnostics": e} + + try: diags = {} xml = get_rrd(host_ip, record["uuid"]) if xml: diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 4ce8d819b..d384aa003 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -281,34 +281,23 @@ class VMOps(object): def suspend(self, instance, callback): """suspend the specified instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: - raise Exception(_("suspend: instance not present %s") % - instance_name) + vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.suspend', vm) self._wait_with_callback(task, callback) def resume(self, instance, callback): """resume the specified instance""" - instance_name = instance.name - vm = VMHelper.lookup(self._session, instance_name) - if vm is None: - raise Exception(_("resume: instance not present %s") % - instance_name) + vm = self._get_vm_opaque_ref(instance) task = self._session.call_xenapi('Async.VM.resume', vm, False, True) self._wait_with_callback(task, callback) def get_info(self, instance_id): """Return data about VM instance""" - vm = VMHelper.lookup(self._session, instance_id) - if vm is None: - raise exception.NotFound(_('Instance not' - ' found %s') % instance_id) + vm = self._get_vm_opaque_ref(instance) rec = self._session.get_xenapi().VM.get_record(vm) return VMHelper.compile_info(rec) - def get_diagnostics(self, instance_id): + def get_diagnostics(self, instance): """Return data about VM diagnostics""" vm = self._get_vm_opaque_ref(instance) rec = self._session.get_xenapi().VM.get_record(vm) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 3baae6188..f4dd7055c 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -172,9 +172,9 @@ class XenAPIConnection(object): """Return data about VM instance""" return self._vmops.get_info(instance_id) - def get_diagnostics(self, instance_id): + def get_diagnostics(self, instance): """Return data about VM diagnostics""" - return self._vmops.get_diagnostics(instance_id) + return self._vmops.get_diagnostics(instance) def get_console_output(self, instance): """Return snapshot of console""" @@ -254,7 +254,7 @@ class XenAPISession(object): status = self._session.xenapi.task.get_status(task) action = dict( instance_id=int(id), - action=name, + action=name[0:255], # Ensure action is never > 255 error=None) if status == "pending": return diff --git a/run_tests.py b/run_tests.py new file mode 100644 index 000000000..5b8617f63 --- /dev/null +++ b/run_tests.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# 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. + + +import os +import unittest +import sys + +from nose import config +from nose import result +from nose import core + + +class NovaTestResult(result.TextTestResult): + def __init__(self, *args, **kw): + result.TextTestResult.__init__(self, *args, **kw) + self._last_case = None + + def getDescription(self, test): + return str(test) + + def startTest(self, test): + unittest.TestResult.startTest(self, test) + current_case = test.test.__class__.__name__ + + if self.showAll: + if current_case != self._last_case: + self.stream.writeln(current_case) + self._last_case = current_case + + self.stream.write( + ' %s' % str(test.test._testMethodName).ljust(60)) + self.stream.flush() + + +class NovaTestRunner(core.TextTestRunner): + def _makeResult(self): + return NovaTestResult(self.stream, + self.descriptions, + self.verbosity, + self.config) + + +if __name__ == '__main__': + c = config.Config(stream=sys.stdout, + env=os.environ, + verbosity=3) + + runner = NovaTestRunner(stream=c.stream, + verbosity=c.verbosity, + config=c) + sys.exit(not core.run(config=c, testRunner=runner)) diff --git a/run_tests.sh b/run_tests.sh index ffb0b6295..fe703fece 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -32,16 +32,17 @@ never_venv=0 force=0 noseargs= + for arg in "$@"; do process_option $arg done -NOSETESTS="nosetests -v $noseargs" +NOSETESTS="python run_tests.py $noseargs" if [ $never_venv -eq 1 ]; then # Just run the test suites in current environment rm -f nova.sqlite - $NOSETESTS + $NOSETESTS 2> run_tests.err.log exit fi @@ -53,7 +54,7 @@ fi if [ -e ${venv} ]; then ${with_venv} rm -f nova.sqlite - ${with_venv} $NOSETESTS + ${with_venv} $NOSETESTS 2> run_tests.err.log else if [ $always_venv -eq 1 ]; then # Automatically install the virtualenv @@ -66,10 +67,10 @@ else python tools/install_venv.py else rm -f nova.sqlite - $NOSETESTS + $NOSETESTS 2> run_tests.err.log exit fi fi ${with_venv} rm -f nova.sqlite - ${with_venv} $NOSETESTS + ${with_venv} $NOSETESTS 2> run_tests.err.log fi |
