summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorChris Behrens <cbehrens@codestud.com>2011-05-13 16:45:42 +0000
committerChris Behrens <cbehrens@codestud.com>2011-05-13 16:45:42 +0000
commitbe83d7317ee55ae0ef436f09f25beef2eccee5a6 (patch)
tree87d4f53c8bd7c5c4056b131b3fc039ee0740f1d6 /nova
parent7dc76508d38370f3cf68029fae2ffb7749a580b2 (diff)
parent0805521c79f934ba54f839be64a2c43ed177612d (diff)
downloadnova-be83d7317ee55ae0ef436f09f25beef2eccee5a6.tar.gz
nova-be83d7317ee55ae0ef436f09f25beef2eccee5a6.tar.xz
nova-be83d7317ee55ae0ef436f09f25beef2eccee5a6.zip
Merged trunk
Diffstat (limited to 'nova')
-rw-r--r--nova/CA/openssl.cnf.tmpl2
-rw-r--r--nova/api/ec2/cloud.py2
-rw-r--r--nova/api/ec2/ec2utils.py5
-rw-r--r--nova/compute/api.py22
-rw-r--r--nova/compute/manager.py27
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/001_austin.py8
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py7
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py7
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py7
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py7
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py6
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py7
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py9
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py6
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/010_add_os_type_to_instances.py7
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/011_live_migration.py5
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/012_add_ipv6_flatmanager.py8
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/013_add_flavors_to_migrations.py6
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py7
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/015_add_auto_assign_to_floating_ips.py6
-rw-r--r--nova/exception.py4
-rw-r--r--nova/scheduler/host_filter.py22
-rw-r--r--nova/tests/test_api.py19
-rw-r--r--nova/tests/test_compute.py9
-rw-r--r--nova/tests/test_host_filter.py26
-rw-r--r--nova/tests/test_utils.py2
-rw-r--r--nova/tests/test_xenapi.py50
-rw-r--r--nova/utils.py9
-rw-r--r--nova/virt/hyperv.py8
-rw-r--r--nova/virt/libvirt_conn.py8
-rw-r--r--nova/virt/xenapi_conn.py80
31 files changed, 279 insertions, 119 deletions
diff --git a/nova/CA/openssl.cnf.tmpl b/nova/CA/openssl.cnf.tmpl
index b80fadf40..f87d9f3b2 100644
--- a/nova/CA/openssl.cnf.tmpl
+++ b/nova/CA/openssl.cnf.tmpl
@@ -46,7 +46,7 @@ policy = policy_match
# RHEL 6 and Fedora 14 (using openssl-1.0.0-4.el6.x86_64 or
# openssl-1.0.0d-1.fc14.x86_64)
[ policy_match ]
-countryName = match
+countryName = supplied
stateOrProvinceName = supplied
organizationName = optional
organizationalUnitName = optional
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 092b80fa2..be5dd38a0 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -906,7 +906,7 @@ class CloudController(object):
try:
internal_id = ec2utils.ec2_id_to_id(ec2_id)
return self.image_service.show(context, internal_id)
- except ValueError:
+ except (exception.InvalidEc2Id, exception.ImageNotFound):
try:
return self.image_service.show_by_name(context, ec2_id)
except exception.NotFound:
diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py
index 1ac48163c..163aa4ed2 100644
--- a/nova/api/ec2/ec2utils.py
+++ b/nova/api/ec2/ec2utils.py
@@ -21,7 +21,10 @@ from nova import exception
def ec2_id_to_id(ec2_id):
"""Convert an ec2 ID (i-[base 16 number]) to an instance id (int)"""
- return int(ec2_id.split('-')[-1], 16)
+ try:
+ return int(ec2_id.split('-')[-1], 16)
+ except ValueError:
+ raise exception.InvalidEc2Id(ec2_id=ec2_id)
def id_to_ec2_id(instance_id, template='i-%08x'):
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 63884be97..a12f8d515 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -19,6 +19,7 @@
"""Handles all requests relating to instances (guest vms)."""
import datetime
+import eventlet
import re
import time
@@ -42,6 +43,8 @@ LOG = logging.getLogger('nova.compute.api')
FLAGS = flags.FLAGS
flags.DECLARE('vncproxy_topic', 'nova.vnc')
+flags.DEFINE_integer('find_host_timeout', 30,
+ 'Timeout after NN seconds when looking for a host.')
def generate_default_hostname(instance_id):
@@ -484,7 +487,7 @@ class API(base.Base):
def _find_host(self, context, instance_id):
"""Find the host associated with an instance."""
- for attempts in xrange(10):
+ for attempts in xrange(FLAGS.find_host_timeout):
instance = self.get(context, instance_id)
host = instance["host"]
if host:
@@ -493,6 +496,15 @@ class API(base.Base):
raise exception.Error(_("Unable to find host for Instance %s")
% instance_id)
+ def _set_admin_password(self, context, instance_id, password):
+ """Set the root/admin password for the given instance."""
+ host = self._find_host(context, instance_id)
+
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "set_admin_password",
+ "args": {"instance_id": instance_id, "new_pass": password}})
+
def snapshot(self, context, instance_id, name):
"""Snapshot the given instance.
@@ -646,12 +658,8 @@ class API(base.Base):
def set_admin_password(self, context, instance_id, password=None):
"""Set the root/admin password for the given instance."""
- host = self._find_host(context, instance_id)
-
- rpc.cast(context,
- self.db.queue_get_for(context, FLAGS.compute_topic, host),
- {"method": "set_admin_password",
- "args": {"instance_id": instance_id, "new_pass": password}})
+ eventlet.spawn_n(self._set_admin_password(context, instance_id,
+ password))
def inject_file(self, context, instance_id):
"""Write a file to the given instance."""
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index ae5b50ef3..923feaa59 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -77,7 +77,8 @@ flags.DEFINE_integer("rescue_timeout", 0,
" Set to 0 to disable.")
flags.DEFINE_bool('auto_assign_floating_ip', False,
'Autoassigning floating ip to VM')
-
+flags.DEFINE_integer('host_state_interval', 120,
+ 'Interval in seconds for querying the host status')
LOG = logging.getLogger('nova.compute.manager')
@@ -131,6 +132,7 @@ class ComputeManager(manager.SchedulerDependentManager):
self.network_manager = utils.import_object(FLAGS.network_manager)
self.volume_manager = utils.import_object(FLAGS.volume_manager)
self.network_api = network.API()
+ self._last_host_check = 0
super(ComputeManager, self).__init__(service_name="compute",
*args, **kwargs)
@@ -425,6 +427,12 @@ class ComputeManager(manager.SchedulerDependentManager):
LOG.audit(_("Instance %s: Root password set"),
instance_ref["name"])
break
+ except NotImplementedError:
+ # NOTE(dprince): if the driver doesn't implement
+ # set_admin_password we break to avoid a loop
+ LOG.warn(_('set_admin_password is not implemented '
+ 'by this driver.'))
+ break
except Exception, e:
# Catch all here because this could be anything.
LOG.exception(e)
@@ -1094,6 +1102,13 @@ class ComputeManager(manager.SchedulerDependentManager):
error_list.append(ex)
try:
+ self._report_driver_status()
+ except Exception as ex:
+ LOG.warning(_("Error during report_driver_status(): %s"),
+ unicode(ex))
+ error_list.append(ex)
+
+ try:
self._poll_instance_states(context)
except Exception as ex:
LOG.warning(_("Error during instance poll: %s"),
@@ -1102,6 +1117,16 @@ class ComputeManager(manager.SchedulerDependentManager):
return error_list
+ def _report_driver_status(self):
+ curr_time = time.time()
+ if curr_time - self._last_host_check > FLAGS.host_state_interval:
+ self._last_host_check = curr_time
+ LOG.info(_("Updating host status"))
+ # This will grab info about the host and queue it
+ # to be sent to the Schedulers.
+ self.update_service_capabilities(
+ self.driver.get_host_stats(refresh=True))
+
def _poll_instance_states(self, context):
vm_instances = self.driver.list_instances_detail()
vm_instances = dict((vm.name, vm) for vm in vm_instances)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
index 9e7ab3554..63bbaccc1 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/001_austin.py
@@ -17,15 +17,13 @@
# under the License.
## Table code mostly autogenerated by genmodel.py
-from sqlalchemy import *
-from migrate import *
-
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey
+from sqlalchemy import ForeignKeyConstraint, Integer, MetaData, String
+from sqlalchemy import Table, Text
from nova import log as logging
-
meta = MetaData()
-
auth_tokens = Table('auth_tokens', meta,
Column('created_at', DateTime(timezone=False)),
Column('updated_at', DateTime(timezone=False)),
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
index 413536a59..9bb8a8ada 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
@@ -16,15 +16,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from migrate import *
-
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey
+from sqlalchemy import Integer, MetaData, String, Table, Text
from nova import log as logging
-
meta = MetaData()
-
# Just for the ForeignKey and column creation to succeed, these are not the
# actual definitions of instances or services.
instances = Table('instances', meta,
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py
index 5ba7910f1..8e0de4d2b 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/003_add_label_to_networks.py
@@ -15,15 +15,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from migrate import *
-
-from nova import log as logging
-
+from sqlalchemy import Column, Integer, MetaData, String, Table
meta = MetaData()
-
networks = Table('networks', meta,
Column('id', Integer(), primary_key=True, nullable=False),
)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py b/nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py
index ade981687..0abea374c 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/004_add_zone_tables.py
@@ -13,15 +13,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from migrate import *
-
+from sqlalchemy import Boolean, Column, DateTime, Integer
+from sqlalchemy import MetaData, String, Table
from nova import log as logging
-
meta = MetaData()
-
#
# New Tables
#
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py b/nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py
index 4cb07e0d8..a1a86e3b4 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/005_add_instance_metadata.py
@@ -15,15 +15,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from migrate import *
-
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer
+from sqlalchemy import MetaData, String, Table
from nova import log as logging
-
meta = MetaData()
-
# Just for the ForeignKey and column creation to succeed, these are not the
# actual definitions of instances or services.
instances = Table('instances', meta,
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py b/nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py
index 705fc8ff3..4627d3332 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/006_add_provider_data_to_volumes.py
@@ -15,11 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from migrate import *
-
-from nova import log as logging
-
+from sqlalchemy import Column, Integer, MetaData, String, Table
meta = MetaData()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py
index 427934d53..6f2668040 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py
@@ -13,15 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from migrate import *
-
-from nova import log as logging
-
+from sqlalchemy import Column, Integer, MetaData, String, Table
meta = MetaData()
-
# Table stub-definitions
# Just for the ForeignKey and column creation to succeed, these are not the
# actual definitions of instances or services.
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py b/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py
index 5e2cb69d9..63999f6ff 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py
@@ -13,15 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from migrate import *
-
-from nova import api
-from nova import db
+from sqlalchemy import Boolean, Column, DateTime, Integer
+from sqlalchemy import MetaData, String, Table
from nova import log as logging
-import datetime
-
meta = MetaData()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py
index 4fda525f1..0f2d0079a 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/009_add_instance_migrations.py
@@ -15,12 +15,10 @@
# License for the specific language governing permissions and limitations
# under the License.from sqlalchemy import *
-from sqlalchemy import *
-from migrate import *
-
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer
+from sqlalchemy import MetaData, String, Table
from nova import log as logging
-
meta = MetaData()
# Just for the ForeignKey and column creation to succeed, these are not the
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/010_add_os_type_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/010_add_os_type_to_instances.py
index eb3066894..a5b80586e 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/010_add_os_type_to_instances.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/010_add_os_type_to_instances.py
@@ -14,12 +14,7 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from sqlalchemy.sql import text
-from migrate import *
-
-from nova import log as logging
-
+from sqlalchemy import Column, Integer, MetaData, String, Table
meta = MetaData()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/011_live_migration.py b/nova/db/sqlalchemy/migrate_repo/versions/011_live_migration.py
index 23ccccb4e..b2b0256d2 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/011_live_migration.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/011_live_migration.py
@@ -16,10 +16,9 @@
# License for the specific language governing permissions and limitations
# under the License.
-from migrate import *
+from sqlalchemy import Boolean, Column, DateTime, Integer, MetaData
+from sqlalchemy import Table, Text
from nova import log as logging
-from sqlalchemy import *
-
meta = MetaData()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/012_add_ipv6_flatmanager.py b/nova/db/sqlalchemy/migrate_repo/versions/012_add_ipv6_flatmanager.py
index e87085668..10d250522 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/012_add_ipv6_flatmanager.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/012_add_ipv6_flatmanager.py
@@ -13,15 +13,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from migrate import *
-
-from nova import log as logging
-
+from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer
+from sqlalchemy import MetaData, String, Table
meta = MetaData()
-
# Table stub-definitions
# Just for the ForeignKey and column creation to succeed, these are not the
# actual definitions of instances or services.
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/013_add_flavors_to_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/013_add_flavors_to_migrations.py
index 3fb92e85c..7246839b7 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/013_add_flavors_to_migrations.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/013_add_flavors_to_migrations.py
@@ -15,11 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.from sqlalchemy import *
-from sqlalchemy import *
-from migrate import *
-
-from nova import log as logging
-
+from sqlalchemy import Column, Integer, MetaData, Table
meta = MetaData()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py
index 334d1f255..62216be12 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/014_add_instance_type_id_to_instances.py
@@ -14,16 +14,11 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from sqlalchemy.sql import text
-from migrate import *
-
+from sqlalchemy import Column, Integer, MetaData, String, Table
#from nova import log as logging
-
meta = MetaData()
-
c_instance_type = Column('instance_type',
String(length=255, convert_unicode=False,
assert_unicode=None, unicode_error=None,
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/015_add_auto_assign_to_floating_ips.py b/nova/db/sqlalchemy/migrate_repo/versions/015_add_auto_assign_to_floating_ips.py
index 29b26b3dd..375760c84 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/015_add_auto_assign_to_floating_ips.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/015_add_auto_assign_to_floating_ips.py
@@ -15,14 +15,10 @@
# License for the specific language governing permissions and limitations
# under the License.
-from sqlalchemy import *
-from sqlalchemy.sql import text
-from migrate import *
-
+from sqlalchemy import Boolean, Column, MetaData, Table
meta = MetaData()
-
c_auto_assigned = Column('auto_assigned', Boolean, default=False)
diff --git a/nova/exception.py b/nova/exception.py
index 9905fb19b..cf6069454 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -244,6 +244,10 @@ class InstanceUnacceptable(Invalid):
message = _("Instance %(instance_id)s is unacceptable") + ": %(reason)s"
+class InvalidEc2Id(Invalid):
+ message = _("Ec2 id %(ec2_id)s is unacceptable.")
+
+
class NotFound(NovaException):
message = _("Resource could not be found.")
diff --git a/nova/scheduler/host_filter.py b/nova/scheduler/host_filter.py
index ed8e65c77..483f3225c 100644
--- a/nova/scheduler/host_filter.py
+++ b/nova/scheduler/host_filter.py
@@ -96,8 +96,8 @@ class FlavorFilter(HostFilter):
selected_hosts = []
for host, services in zone_manager.service_states.iteritems():
capabilities = services.get('compute', {})
- host_ram_mb = capabilities['host_memory']['free']
- disk_bytes = capabilities['disk']['available']
+ host_ram_mb = capabilities['host_memory_free']
+ disk_bytes = capabilities['disk_available']
if host_ram_mb >= instance_type['memory_mb'] and \
disk_bytes >= instance_type['local_gb']:
selected_hosts.append((host, capabilities))
@@ -106,16 +106,16 @@ class FlavorFilter(HostFilter):
#host entries (currently) are like:
# {'host_name-description': 'Default install of XenServer',
# 'host_hostname': 'xs-mini',
-# 'host_memory': {'total': 8244539392,
-# 'overhead': 184225792,
-# 'free': 3868327936,
-# 'free-computed': 3840843776},
+# 'host_memory_total': 8244539392,
+# 'host_memory_overhead': 184225792,
+# 'host_memory_free': 3868327936,
+# 'host_memory_free_computed': 3840843776},
# 'host_other-config': {},
# 'host_ip_address': '192.168.1.109',
# 'host_cpu_info': {},
-# 'disk': {'available': 32954957824,
-# 'total': 50394562560,
-# 'used': 17439604736},
+# 'disk_available': 32954957824,
+# 'disk_total': 50394562560,
+# 'disk_used': 17439604736},
# 'host_uuid': 'cedb9b39-9388-41df-8891-c5c9a0c0fe5f',
# 'host_name-label': 'xs-mini'}
@@ -221,8 +221,8 @@ class JsonFilter(HostFilter):
required_ram = instance_type['memory_mb']
required_disk = instance_type['local_gb']
query = ['and',
- ['>=', '$compute.host_memory.free', required_ram],
- ['>=', '$compute.disk.available', required_disk]
+ ['>=', '$compute.host_memory_free', required_ram],
+ ['>=', '$compute.disk_available', required_disk]
]
return (self._full_name(), json.dumps(query))
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index fa0e56597..97f401b87 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -28,10 +28,12 @@ import StringIO
import webob
from nova import context
+from nova import exception
from nova import test
from nova.api import ec2
-from nova.api.ec2 import cloud
from nova.api.ec2 import apirequest
+from nova.api.ec2 import cloud
+from nova.api.ec2 import ec2utils
from nova.auth import manager
@@ -101,6 +103,21 @@ class XmlConversionTestCase(test.TestCase):
self.assertEqual(conv('-0'), 0)
+class Ec2utilsTestCase(test.TestCase):
+ def test_ec2_id_to_id(self):
+ self.assertEqual(ec2utils.ec2_id_to_id('i-0000001e'), 30)
+ self.assertEqual(ec2utils.ec2_id_to_id('ami-1d'), 29)
+
+ def test_bad_ec2_id(self):
+ self.assertRaises(exception.InvalidEc2Id,
+ ec2utils.ec2_id_to_id,
+ 'badone')
+
+ def test_id_to_ec2_id(self):
+ self.assertEqual(ec2utils.id_to_ec2_id(30), 'i-0000001e')
+ self.assertEqual(ec2utils.id_to_ec2_id(29, 'ami-%08x'), 'ami-0000001d')
+
+
class ApiEc2TestCase(test.TestCase):
"""Unit test for the cloud controller on an EC2 API"""
def setUp(self):
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 393110791..55e7ae0c4 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -21,6 +21,7 @@ Tests For Compute
import datetime
import mox
+import stubout
from nova import compute
from nova import context
@@ -52,6 +53,10 @@ class FakeTime(object):
self.counter += t
+def nop_report_driver_status(self):
+ pass
+
+
class ComputeTestCase(test.TestCase):
"""Test case for compute"""
def setUp(self):
@@ -649,6 +654,10 @@ class ComputeTestCase(test.TestCase):
def test_run_kill_vm(self):
"""Detect when a vm is terminated behind the scenes"""
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(compute_manager.ComputeManager,
+ '_report_driver_status', nop_report_driver_status)
+
instance_id = self._create_instance()
self.compute.run_instance(self.context, instance_id)
diff --git a/nova/tests/test_host_filter.py b/nova/tests/test_host_filter.py
index 31e40ae1d..c029d41e6 100644
--- a/nova/tests/test_host_filter.py
+++ b/nova/tests/test_host_filter.py
@@ -43,16 +43,16 @@ class HostFilterTestCase(test.TestCase):
# which means ... don't go above 10 hosts.
return {'host_name-description': 'XenServer %s' % multiplier,
'host_hostname': 'xs-%s' % multiplier,
- 'host_memory': {'total': 100,
- 'overhead': 10,
- 'free': 10 + multiplier * 10,
- 'free-computed': 10 + multiplier * 10},
+ 'host_memory_total': 100,
+ 'host_memory_overhead': 10,
+ 'host_memory_free': 10 + multiplier * 10,
+ 'host_memory_free-computed': 10 + multiplier * 10,
'host_other-config': {},
'host_ip_address': '192.168.1.%d' % (100 + multiplier),
'host_cpu_info': {},
- 'disk': {'available': 100 + multiplier * 100,
- 'total': 1000,
- 'used': 0},
+ 'disk_available': 100 + multiplier * 100,
+ 'disk_total': 1000,
+ 'disk_used': 0,
'host_uuid': 'xxx-%d' % multiplier,
'host_name-label': 'xs-%s' % multiplier}
@@ -131,12 +131,12 @@ class HostFilterTestCase(test.TestCase):
raw = ['or',
['and',
- ['<', '$compute.host_memory.free', 30],
- ['<', '$compute.disk.available', 300]
+ ['<', '$compute.host_memory_free', 30],
+ ['<', '$compute.disk_available', 300]
],
['and',
- ['>', '$compute.host_memory.free', 70],
- ['>', '$compute.disk.available', 700]
+ ['>', '$compute.host_memory_free', 70],
+ ['>', '$compute.disk_available', 700]
]
]
cooked = json.dumps(raw)
@@ -149,7 +149,7 @@ class HostFilterTestCase(test.TestCase):
self.assertEquals('host%02d' % index, host)
raw = ['not',
- ['=', '$compute.host_memory.free', 30],
+ ['=', '$compute.host_memory_free', 30],
]
cooked = json.dumps(raw)
hosts = driver.filter_hosts(self.zone_manager, cooked)
@@ -160,7 +160,7 @@ class HostFilterTestCase(test.TestCase):
for index, host in zip([1, 2, 4, 5, 6, 7, 8, 9, 10], just_hosts):
self.assertEquals('host%02d' % index, host)
- raw = ['in', '$compute.host_memory.free', 20, 40, 60, 80, 100]
+ raw = ['in', '$compute.host_memory_free', 20, 40, 60, 80, 100]
cooked = json.dumps(raw)
hosts = driver.filter_hosts(self.zone_manager, cooked)
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py
index e7b5c826e..8f7e83c3e 100644
--- a/nova/tests/test_utils.py
+++ b/nova/tests/test_utils.py
@@ -17,9 +17,9 @@
import os
import tempfile
+from nova import exception
from nova import test
from nova import utils
-from nova import exception
class ExecuteTestCase(test.TestCase):
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index a4e679817..be1e35697 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -18,6 +18,7 @@
import eventlet
import functools
+import json
import os
import re
import stubout
@@ -688,3 +689,52 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase):
self.fake_instance.image_id = glance_stubs.FakeGlance.IMAGE_VHD
self.fake_instance.kernel_id = None
self.assert_disk_type(vm_utils.ImageType.DISK_VHD)
+
+
+class FakeXenApi(object):
+ """Fake XenApi for testing HostState."""
+
+ class FakeSR(object):
+ def get_record(self, ref):
+ return {'virtual_allocation': 10000,
+ 'physical_utilisation': 20000}
+
+ SR = FakeSR()
+
+
+class FakeSession(object):
+ """Fake Session class for HostState testing."""
+
+ def async_call_plugin(self, *args):
+ return None
+
+ def wait_for_task(self, *args):
+ vm = {'total': 10,
+ 'overhead': 20,
+ 'free': 30,
+ 'free-computed': 40}
+ return json.dumps({'host_memory': vm})
+
+ def get_xenapi(self):
+ return FakeXenApi()
+
+
+class HostStateTestCase(test.TestCase):
+ """Tests HostState, which holds metrics from XenServer that get
+ reported back to the Schedulers."""
+
+ def _fake_safe_find_sr(self, session):
+ """None SR ref since we're ignoring it in FakeSR."""
+ return None
+
+ def test_host_state(self):
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(vm_utils, 'safe_find_sr', self._fake_safe_find_sr)
+ host_state = xenapi_conn.HostState(FakeSession())
+ stats = host_state._stats
+ self.assertEquals(stats['disk_total'], 10000)
+ self.assertEquals(stats['disk_used'], 20000)
+ self.assertEquals(stats['host_memory_total'], 10)
+ self.assertEquals(stats['host_memory_overhead'], 20)
+ self.assertEquals(stats['host_memory_free'], 30)
+ self.assertEquals(stats['host_memory_free_computed'], 40)
diff --git a/nova/utils.py b/nova/utils.py
index 44ee1b623..0c469b1de 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -232,9 +232,12 @@ def default_flagfile(filename='nova.conf'):
# turn relative filename into an absolute path
script_dir = os.path.dirname(inspect.stack()[-1][1])
filename = os.path.abspath(os.path.join(script_dir, filename))
- if os.path.exists(filename):
- flagfile = ['--flagfile=%s' % filename]
- sys.argv = sys.argv[:1] + flagfile + sys.argv[1:]
+ if not os.path.exists(filename):
+ filename = "./nova.conf"
+ if not os.path.exists(filename):
+ filename = '/etc/nova/nova.conf'
+ flagfile = ['--flagfile=%s' % filename]
+ sys.argv = sys.argv[:1] + flagfile + sys.argv[1:]
def debug(arg):
diff --git a/nova/virt/hyperv.py b/nova/virt/hyperv.py
index 9026e737e..1142e97a4 100644
--- a/nova/virt/hyperv.py
+++ b/nova/virt/hyperv.py
@@ -486,3 +486,11 @@ class HyperVConnection(driver.ComputeDriver):
def update_available_resource(self, ctxt, host):
"""This method is supported only by libvirt."""
return
+
+ def update_host_status(self):
+ """See xenapi_conn.py implementation."""
+ pass
+
+ def get_host_stats(self, refresh=False):
+ """See xenapi_conn.py implementation."""
+ pass
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 9780c69a6..555e44ce2 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1582,6 +1582,14 @@ class LibvirtConnection(driver.ComputeDriver):
"""See comments of same method in firewall_driver."""
self.firewall_driver.unfilter_instance(instance_ref)
+ def update_host_status(self):
+ """See xenapi_conn.py implementation."""
+ pass
+
+ def get_host_stats(self, refresh=False):
+ """See xenapi_conn.py implementation."""
+ pass
+
class FirewallDriver(object):
def prepare_instance_filter(self, instance, network_info=None):
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 5dc6b034f..eb572f295 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -57,6 +57,8 @@ reactor thread if the VM.get_by_name_label or VM.get_record calls block.
- suffix "_rec" for record objects
"""
+import json
+import random
import sys
import urlparse
import xmlrpclib
@@ -67,10 +69,12 @@ from eventlet import timeout
from nova import context
from nova import db
+from nova import exception
from nova import utils
from nova import flags
from nova import log as logging
from nova.virt import driver
+from nova.virt.xenapi import vm_utils
from nova.virt.xenapi.vmops import VMOps
from nova.virt.xenapi.volumeops import VolumeOps
@@ -168,6 +172,13 @@ class XenAPIConnection(driver.ComputeDriver):
session = XenAPISession(url, user, pw)
self._vmops = VMOps(session)
self._volumeops = VolumeOps(session)
+ self._host_state = None
+
+ @property
+ def HostState(self):
+ if not self._host_state:
+ self._host_state = HostState(self.session)
+ return self._host_state
def init_host(self, host):
#FIXME(armando): implement this
@@ -315,6 +326,16 @@ class XenAPIConnection(driver.ComputeDriver):
"""This method is supported only by libvirt."""
raise NotImplementedError('This method is supported only by libvirt.')
+ def update_host_status(self):
+ """Update the status info of the host, and return those values
+ to the calling program."""
+ return self.HostState.update_status()
+
+ def get_host_stats(self, refresh=False):
+ """Return the current state of the host. If 'refresh' is
+ True, run the update first."""
+ return self.HostState.get_host_stats(refresh=refresh)
+
class XenAPISession(object):
"""The session to invoke XenAPI SDK calls"""
@@ -430,6 +451,65 @@ class XenAPISession(object):
raise
+class HostState(object):
+ """Manages information about the XenServer host this compute
+ node is running on.
+ """
+ def __init__(self, session):
+ super(HostState, self).__init__()
+ self._session = session
+ self._stats = {}
+ self.update_status()
+
+ def get_host_stats(self, refresh=False):
+ """Return the current state of the host. If 'refresh' is
+ True, run the update first.
+ """
+ if refresh:
+ self.update_status()
+ return self._stats
+
+ def update_status(self):
+ """Since under Xenserver, a compute node runs on a given host,
+ we can get host status information using xenapi.
+ """
+ LOG.debug(_("Updating host stats"))
+ # Make it something unlikely to match any actual instance ID
+ task_id = random.randint(-80000, -70000)
+ task = self._session.async_call_plugin("xenhost", "host_data", {})
+ task_result = self._session.wait_for_task(task, task_id)
+ if not task_result:
+ task_result = json.dumps("")
+ try:
+ data = json.loads(task_result)
+ except ValueError as e:
+ # Invalid JSON object
+ LOG.error(_("Unable to get updated status: %s") % e)
+ return
+ # Get the SR usage
+ try:
+ sr_ref = vm_utils.safe_find_sr(self._session)
+ except exception.NotFound as e:
+ # No SR configured
+ LOG.error(_("Unable to get SR for this host: %s") % e)
+ return
+ sr_rec = self._session.get_xenapi().SR.get_record(sr_ref)
+ total = int(sr_rec["virtual_allocation"])
+ used = int(sr_rec["physical_utilisation"])
+ data["disk_total"] = total
+ data["disk_used"] = used
+ data["disk_available"] = total - used
+ host_memory = data.get('host_memory', None)
+ if host_memory:
+ data["host_memory_total"] = host_memory.get('total', 0)
+ data["host_memory_overhead"] = host_memory.get('overhead', 0)
+ data["host_memory_free"] = host_memory.get('free', 0)
+ data["host_memory_free_computed"] = \
+ host_memory.get('free-computed', 0)
+ del data['host_memory']
+ self._stats = data
+
+
def _parse_xmlrpc_value(val):
"""Parse the given value as if it were an XML-RPC value. This is
sometimes used as the format for the task.result field."""