summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEd Leafe <ed@leafe.com>2011-01-03 09:56:49 -0600
committerEd Leafe <ed@leafe.com>2011-01-03 09:56:49 -0600
commit5b240fe5d963e195525c42bf097d79b3f003c8d4 (patch)
tree6da1ba783f3d713d5fad37d0ed693e0dc1086a60
parent108352d5c132f6accc79974d8c646a2bc7d4f127 (diff)
parent0e88a58cf95bf9298a52d132cd1eb02f29c6bfe1 (diff)
merged latest trunk
-rw-r--r--.bzrignore1
-rw-r--r--nova/api/openstack/__init__.py2
-rw-r--r--nova/api/openstack/servers.py10
-rw-r--r--nova/compute/api.py14
-rw-r--r--nova/compute/manager.py9
-rw-r--r--nova/db/api.py5
-rw-r--r--nova/db/sqlalchemy/api.py12
-rw-r--r--nova/db/sqlalchemy/models.py21
-rw-r--r--nova/tests/api/openstack/test_servers.py16
-rw-r--r--nova/tests/test_xenapi.py6
-rw-r--r--nova/virt/fake.py3
-rw-r--r--nova/virt/libvirt_conn.py3
-rw-r--r--nova/virt/xenapi/vm_utils.py4
-rw-r--r--nova/virt/xenapi/vmops.py19
-rw-r--r--nova/virt/xenapi_conn.py6
-rw-r--r--run_tests.py68
-rwxr-xr-xrun_tests.sh11
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