Merge from trunk

This commit is contained in:
Ryan Lane
2011-01-06 20:14:22 +00:00
15 changed files with 592 additions and 58 deletions

View File

@@ -6,6 +6,7 @@ keys
networks networks
nova.sqlite nova.sqlite
CA/cacert.pem CA/cacert.pem
CA/crl.pem
CA/index.txt* CA/index.txt*
CA/openssl.cnf CA/openssl.cnf
CA/serial* CA/serial*

View File

@@ -27,5 +27,7 @@
<vishvananda@gmail.com> <root@ubuntu> <vishvananda@gmail.com> <root@ubuntu>
<sleepsonthefloor@gmail.com> <root@tonbuntu> <sleepsonthefloor@gmail.com> <root@tonbuntu>
<rlane@wikimedia.org> <laner@controller> <rlane@wikimedia.org> <laner@controller>
<rconradharris@gmail.com> <rick.harris@rackspace.com>
<corywright@gmail.com> <cory.wright@rackspace.com> <corywright@gmail.com> <cory.wright@rackspace.com>
<ant@openstack.org> <amesserl@rackspace.com> <ant@openstack.org> <amesserl@rackspace.com>
<chiradeep@cloud.com> <chiradeep@chiradeep-lt2>

View File

@@ -3,6 +3,7 @@ Anne Gentle <anne@openstack.org>
Anthony Young <sleepsonthefloor@gmail.com> Anthony Young <sleepsonthefloor@gmail.com>
Antony Messerli <ant@openstack.org> Antony Messerli <ant@openstack.org>
Armando Migliaccio <Armando.Migliaccio@eu.citrix.com> Armando Migliaccio <Armando.Migliaccio@eu.citrix.com>
Chiradeep Vittal <chiradeep@cloud.com>
Chris Behrens <cbehrens@codestud.com> Chris Behrens <cbehrens@codestud.com>
Chmouel Boudjnah <chmouel@chmouel.com> Chmouel Boudjnah <chmouel@chmouel.com>
Cory Wright <corywright@gmail.com> Cory Wright <corywright@gmail.com>
@@ -27,6 +28,7 @@ Michael Gundlach <michael.gundlach@rackspace.com>
Monty Taylor <mordred@inaugust.com> Monty Taylor <mordred@inaugust.com>
Paul Voccio <paul@openstack.org> Paul Voccio <paul@openstack.org>
Rick Clark <rick@openstack.org> Rick Clark <rick@openstack.org>
Rick Harris <rconradharris@gmail.com>
Ryan Lane <rlane@wikimedia.org> Ryan Lane <rlane@wikimedia.org>
Ryan Lucio <rlucio@internap.com> Ryan Lucio <rlucio@internap.com>
Salvatore Orlando <salvatore.orlando@eu.citrix.com> Salvatore Orlando <salvatore.orlando@eu.citrix.com>

109
bin/nova-api-paste Executable file
View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python
# pylint: disable-msg=C0103
# 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.
"""Starter script for Nova API."""
import gettext
import logging
import os
import sys
from paste import deploy
# If ../nova/__init__.py exists, add ../ to Python search path, so that
# it will override what happens to be installed in /usr/(local/)lib/python...
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
os.pardir,
os.pardir))
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
sys.path.insert(0, possible_topdir)
gettext.install('nova', unicode=1)
from nova import flags
from nova import wsgi
LOG = logging.getLogger('nova.api')
LOG.setLevel(logging.DEBUG)
LOG.addHandler(logging.StreamHandler())
FLAGS = flags.FLAGS
API_ENDPOINTS = ['ec2', 'openstack']
def load_configuration(paste_config):
"""Load the paste configuration from the config file and return it."""
config = None
# Try each known name to get the global DEFAULTS, which will give ports
for name in API_ENDPOINTS:
try:
config = deploy.appconfig("config:%s" % paste_config, name=name)
except LookupError:
pass
if config:
verbose = config.get('verbose', None)
if verbose:
FLAGS.verbose = int(verbose) == 1
if FLAGS.verbose:
logging.getLogger().setLevel(logging.DEBUG)
return config
LOG.debug(_("Paste config at %s has no secion for known apis"),
paste_config)
print _("Paste config at %s has no secion for any known apis") % \
paste_config
os.exit(1)
def launch_api(paste_config_file, section, server, port, host):
"""Launch an api server from the specified port and IP."""
LOG.debug(_("Launching %s api on %s:%s"), section, host, port)
app = deploy.loadapp('config:%s' % paste_config_file, name=section)
server.start(app, int(port), host)
def run_app(paste_config_file):
LOG.debug(_("Using paste.deploy config at: %s"), configfile)
config = load_configuration(paste_config_file)
LOG.debug(_("Configuration: %r"), config)
server = wsgi.Server()
ip = config.get('host', '0.0.0.0')
for api in API_ENDPOINTS:
port = config.get("%s_port" % api, None)
if not port:
continue
host = config.get("%s_host" % api, ip)
launch_api(configfile, api, server, port, host)
LOG.debug(_("All api servers launched, now waiting"))
server.wait()
if __name__ == '__main__':
FLAGS(sys.argv)
configfiles = ['/etc/nova/nova-api.conf']
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
configfiles.insert(0,
os.path.join(possible_topdir, 'etc', 'nova-api.conf'))
for configfile in configfiles:
if os.path.exists(configfile):
run_app(configfile)
break
else:
LOG.debug(_("Skipping missing configuration: %s"), configfile)

View File

@@ -53,6 +53,7 @@
CLI interface for nova management. CLI interface for nova management.
""" """
import datetime
import gettext import gettext
import logging import logging
import os import os
@@ -452,6 +453,52 @@ class NetworkCommands(object):
int(network_size), int(vlan_start), int(network_size), int(vlan_start),
int(vpn_start)) int(vpn_start))
class ServiceCommands(object):
"""Enable and disable running services"""
def list(self, host=None, service=None):
"""Show a list of all running services. Filter by host & service name.
args: [host] [service]"""
ctxt = context.get_admin_context()
now = datetime.datetime.utcnow()
services = db.service_get_all(ctxt)
if host:
services = [s for s in services if s['host'] == host]
if service:
services = [s for s in services if s['binary'] == service]
for svc in services:
delta = now - (svc['updated_at'] or svc['created_at'])
alive = (delta.seconds <= 15)
art = (alive and ":-)") or "XXX"
active = 'enabled'
if svc['disabled']:
active = 'disabled'
print "%-10s %-10s %-8s %s %s" % (svc['host'], svc['binary'],
active, art,
svc['updated_at'])
def enable(self, host, service):
"""Enable scheduling for a service
args: host service"""
ctxt = context.get_admin_context()
svc = db.service_get_by_args(ctxt, host, service)
if not svc:
print "Unable to find service"
return
db.service_update(ctxt, svc['id'], {'disabled': False})
def disable(self, host, service):
"""Disable scheduling for a service
args: host service"""
ctxt = context.get_admin_context()
svc = db.service_get_by_args(ctxt, host, service)
if not svc:
print "Unable to find service"
return
db.service_update(ctxt, svc['id'], {'disabled': True})
CATEGORIES = [ CATEGORIES = [
('user', UserCommands), ('user', UserCommands),
('project', ProjectCommands), ('project', ProjectCommands),
@@ -459,7 +506,8 @@ CATEGORIES = [
('shell', ShellCommands), ('shell', ShellCommands),
('vpn', VpnCommands), ('vpn', VpnCommands),
('floating', FloatingIpCommands), ('floating', FloatingIpCommands),
('network', NetworkCommands)] ('network', NetworkCommands),
('service', ServiceCommands)]
def lazy_match(name, key_value_tuples): def lazy_match(name, key_value_tuples):

View File

@@ -23,12 +23,9 @@ import base64
import boto import boto
import httplib import httplib
from nova import flags
from boto.ec2.regioninfo import RegionInfo from boto.ec2.regioninfo import RegionInfo
FLAGS = flags.FLAGS
DEFAULT_CLC_URL = 'http://127.0.0.1:8773' DEFAULT_CLC_URL = 'http://127.0.0.1:8773'
DEFAULT_REGION = 'nova' DEFAULT_REGION = 'nova'
@@ -199,8 +196,8 @@ class NovaAdminClient(object):
self, self,
clc_url=DEFAULT_CLC_URL, clc_url=DEFAULT_CLC_URL,
region=DEFAULT_REGION, region=DEFAULT_REGION,
access_key=FLAGS.aws_access_key_id, access_key=None,
secret_key=FLAGS.aws_secret_access_key, secret_key=None,
**kwargs): **kwargs):
parts = self.split_clc_url(clc_url) parts = self.split_clc_url(clc_url)

View File

@@ -212,6 +212,8 @@ DEFINE_list('region_list',
DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake') DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID') DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key') DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
DEFINE_integer('glance_port', 9292, 'glance port')
DEFINE_string('glance_host', utils.get_my_ip(), 'glance host')
DEFINE_integer('s3_port', 3333, 's3 port') DEFINE_integer('s3_port', 3333, 's3 port')
DEFINE_string('s3_host', utils.get_my_ip(), 's3 host (for infrastructure)') DEFINE_string('s3_host', utils.get_my_ip(), 's3 host (for infrastructure)')
DEFINE_string('s3_dmz', utils.get_my_ip(), 's3 dmz ip (for instances)') DEFINE_string('s3_dmz', utils.get_my_ip(), 's3 dmz ip (for instances)')
@@ -239,6 +241,7 @@ DEFINE_string('cc_dmz', utils.get_my_ip(), 'internal ip of api server')
DEFINE_integer('cc_port', 8773, 'cloud controller port') DEFINE_integer('cc_port', 8773, 'cloud controller port')
DEFINE_string('ec2_suffix', '/services/Cloud', 'suffix for ec2') DEFINE_string('ec2_suffix', '/services/Cloud', 'suffix for ec2')
DEFINE_string('default_project', 'openstack', 'default project for openstack')
DEFINE_string('default_image', 'ami-11111', DEFINE_string('default_image', 'ami-11111',
'default image to use, testing only') 'default image to use, testing only')
DEFINE_string('default_instance_type', 'm1.small', DEFINE_string('default_instance_type', 'm1.small',
@@ -260,6 +263,11 @@ DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'),
DEFINE_string('sql_connection', DEFINE_string('sql_connection',
'sqlite:///$state_path/nova.sqlite', 'sqlite:///$state_path/nova.sqlite',
'connection string for sql database') 'connection string for sql database')
DEFINE_string('sql_idle_timeout',
'3600',
'timeout for idle sql database connections')
DEFINE_integer('sql_max_retries', 12, 'sql connection attempts')
DEFINE_integer('sql_retry_interval', 10, 'sql connection retry interval')
DEFINE_string('compute_manager', 'nova.compute.manager.ComputeManager', DEFINE_string('compute_manager', 'nova.compute.manager.ComputeManager',
'Manager for compute') 'Manager for compute')

View File

@@ -43,6 +43,19 @@ class SimpleScheduler(chance.ChanceScheduler):
def schedule_run_instance(self, context, instance_id, *_args, **_kwargs): def schedule_run_instance(self, context, instance_id, *_args, **_kwargs):
"""Picks a host that is up and has the fewest running instances.""" """Picks a host that is up and has the fewest running instances."""
instance_ref = db.instance_get(context, instance_id) instance_ref = db.instance_get(context, instance_id)
if instance_ref['availability_zone'] and context.is_admin:
zone, _x, host = instance_ref['availability_zone'].partition(':')
service = db.service_get_by_args(context.elevated(), host,
'nova-compute')
if not self.service_is_up(service):
raise driver.WillNotSchedule("Host %s is not alive" % host)
# TODO(vish): this probably belongs in the manager, if we
# can generalize this somehow
now = datetime.datetime.utcnow()
db.instance_update(context, instance_id, {'host': host,
'scheduled_at': now})
return host
results = db.service_get_all_compute_sorted(context) results = db.service_get_all_compute_sorted(context)
for result in results: for result in results:
(service, instance_cores) = result (service, instance_cores) = result
@@ -62,6 +75,19 @@ class SimpleScheduler(chance.ChanceScheduler):
def schedule_create_volume(self, context, volume_id, *_args, **_kwargs): def schedule_create_volume(self, context, volume_id, *_args, **_kwargs):
"""Picks a host that is up and has the fewest volumes.""" """Picks a host that is up and has the fewest volumes."""
volume_ref = db.volume_get(context, volume_id) volume_ref = db.volume_get(context, volume_id)
if (':' in volume_ref['availability_zone']) and context.is_admin:
zone, _x, host = volume_ref['availability_zone'].partition(':')
service = db.service_get_by_args(context.elevated(), host,
'nova-volume')
if not self.service_is_up(service):
raise driver.WillNotSchedule("Host %s not available" % host)
# TODO(vish): this probably belongs in the manager, if we
# can generalize this somehow
now = datetime.datetime.utcnow()
db.volume_update(context, volume_id, {'host': host,
'scheduled_at': now})
return host
results = db.service_get_all_volume_sorted(context) results = db.service_get_all_volume_sorted(context)
for result in results: for result in results:
(service, volume_gigabytes) = result (service, volume_gigabytes) = result

View File

@@ -0,0 +1,71 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2010 Cloud.com, Inc
#
# 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.
"""
Tests For Hyper-V driver
"""
import random
from nova import context
from nova import db
from nova import flags
from nova import test
from nova.auth import manager
from nova.virt import hyperv
FLAGS = flags.FLAGS
FLAGS.connection_type = 'hyperv'
class HyperVTestCase(test.TestCase):
"""Test cases for the Hyper-V driver"""
def setUp(self):
super(HyperVTestCase, self).setUp()
self.manager = manager.AuthManager()
self.user = self.manager.create_user('fake', 'fake', 'fake',
admin=True)
self.project = self.manager.create_project('fake', 'fake', 'fake')
self.context = context.RequestContext(self.user, self.project)
def test_create_destroy(self):
"""Create a VM and destroy it"""
instance = {'internal_id': random.randint(1, 1000000),
'memory_mb': '1024',
'mac_address': '02:12:34:46:56:67',
'vcpus': 2,
'project_id': 'fake',
'instance_type': 'm1.small'}
instance_ref = db.instance_create(self.context, instance)
conn = hyperv.get_connection(False)
conn._create_vm(instance_ref) # pylint: disable-msg=W0212
found = [n for n in conn.list_instances()
if n == instance_ref['name']]
self.assertTrue(len(found) == 1)
info = conn.get_info(instance_ref['name'])
#Unfortunately since the vm is not running at this point,
#we cannot obtain memory information from get_info
self.assertEquals(info['num_cpu'], instance_ref['vcpus'])
conn.destroy(instance_ref)
found = [n for n in conn.list_instances()
if n == instance_ref['name']]
self.assertTrue(len(found) == 0)
def tearDown(self):
super(HyperVTestCase, self).tearDown()
self.manager.delete_project(self.project)
self.manager.delete_user(self.user)

View File

@@ -106,7 +106,7 @@ class CloudTestCase(test.TestCase):
self.cloud.allocate_address(self.context) self.cloud.allocate_address(self.context)
inst = db.instance_create(self.context, {'host': FLAGS.host}) inst = db.instance_create(self.context, {'host': FLAGS.host})
fixed = self.network.allocate_fixed_ip(self.context, inst['id']) fixed = self.network.allocate_fixed_ip(self.context, inst['id'])
ec2_id = cloud.internal_id_to_ec2_id(inst['internal_id']) ec2_id = cloud.id_to_ec2_id(inst['id'])
self.cloud.associate_address(self.context, self.cloud.associate_address(self.context,
instance_id=ec2_id, instance_id=ec2_id,
public_ip=address) public_ip=address)
@@ -127,9 +127,9 @@ class CloudTestCase(test.TestCase):
result = self.cloud.describe_volumes(self.context) result = self.cloud.describe_volumes(self.context)
self.assertEqual(len(result['volumeSet']), 2) self.assertEqual(len(result['volumeSet']), 2)
result = self.cloud.describe_volumes(self.context, result = self.cloud.describe_volumes(self.context,
volume_id=[vol2['ec2_id']]) volume_id=[vol2['id']])
self.assertEqual(len(result['volumeSet']), 1) self.assertEqual(len(result['volumeSet']), 1)
self.assertEqual(result['volumeSet'][0]['volumeId'], vol2['ec2_id']) self.assertEqual(result['volumeSet'][0]['volumeId'], vol2['id'])
db.volume_destroy(self.context, vol1['id']) db.volume_destroy(self.context, vol1['id'])
db.volume_destroy(self.context, vol2['id']) db.volume_destroy(self.context, vol2['id'])
@@ -296,7 +296,7 @@ class CloudTestCase(test.TestCase):
def test_update_of_instance_display_fields(self): def test_update_of_instance_display_fields(self):
inst = db.instance_create(self.context, {}) inst = db.instance_create(self.context, {})
ec2_id = cloud.internal_id_to_ec2_id(inst['internal_id']) ec2_id = cloud.id_to_ec2_id(inst['id'])
self.cloud.update_instance(self.context, ec2_id, self.cloud.update_instance(self.context, ec2_id,
display_name='c00l 1m4g3') display_name='c00l 1m4g3')
inst = db.instance_get(self.context, inst['id']) inst = db.instance_get(self.context, inst['id'])

View File

@@ -22,6 +22,7 @@ Tests For Compute
import datetime import datetime
import logging import logging
from nova import compute
from nova import context from nova import context
from nova import db from nova import db
from nova import exception from nova import exception
@@ -29,7 +30,6 @@ from nova import flags
from nova import test from nova import test
from nova import utils from nova import utils
from nova.auth import manager from nova.auth import manager
from nova.compute import api as compute_api
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@@ -44,7 +44,7 @@ class ComputeTestCase(test.TestCase):
stub_network=True, stub_network=True,
network_manager='nova.network.manager.FlatManager') network_manager='nova.network.manager.FlatManager')
self.compute = utils.import_object(FLAGS.compute_manager) self.compute = utils.import_object(FLAGS.compute_manager)
self.compute_api = compute_api.ComputeAPI() self.compute_api = compute.API()
self.manager = manager.AuthManager() self.manager = manager.AuthManager()
self.user = self.manager.create_user('fake', 'fake', 'fake') self.user = self.manager.create_user('fake', 'fake', 'fake')
self.project = self.manager.create_project('fake', 'fake', 'fake') self.project = self.manager.create_project('fake', 'fake', 'fake')
@@ -72,7 +72,7 @@ class ComputeTestCase(test.TestCase):
"""Verify that an instance cannot be created without a display_name.""" """Verify that an instance cannot be created without a display_name."""
cases = [dict(), dict(display_name=None)] cases = [dict(), dict(display_name=None)]
for instance in cases: for instance in cases:
ref = self.compute_api.create_instances(self.context, ref = self.compute_api.create(self.context,
FLAGS.default_instance_type, None, **instance) FLAGS.default_instance_type, None, **instance)
try: try:
self.assertNotEqual(ref[0].display_name, None) self.assertNotEqual(ref[0].display_name, None)
@@ -80,13 +80,13 @@ class ComputeTestCase(test.TestCase):
db.instance_destroy(self.context, ref[0]['id']) db.instance_destroy(self.context, ref[0]['id'])
def test_create_instance_associates_security_groups(self): def test_create_instance_associates_security_groups(self):
"""Make sure create_instances associates security groups""" """Make sure create associates security groups"""
values = {'name': 'default', values = {'name': 'default',
'description': 'default', 'description': 'default',
'user_id': self.user.id, 'user_id': self.user.id,
'project_id': self.project.id} 'project_id': self.project.id}
group = db.security_group_create(self.context, values) group = db.security_group_create(self.context, values)
ref = self.compute_api.create_instances(self.context, ref = self.compute_api.create(self.context,
FLAGS.default_instance_type, None, security_group=['default']) FLAGS.default_instance_type, None, security_group=['default'])
try: try:
self.assertEqual(len(ref[0]['security_groups']), 1) self.assertEqual(len(ref[0]['security_groups']), 1)
@@ -151,6 +151,14 @@ class ComputeTestCase(test.TestCase):
self.compute.reboot_instance(self.context, instance_id) self.compute.reboot_instance(self.context, instance_id)
self.compute.terminate_instance(self.context, instance_id) self.compute.terminate_instance(self.context, instance_id)
def test_snapshot(self):
"""Ensure instance can be snapshotted"""
instance_id = self._create_instance()
name = "myfakesnapshot"
self.compute.run_instance(self.context, instance_id)
self.compute.snapshot_instance(self.context, instance_id, name)
self.compute.terminate_instance(self.context, instance_id)
def test_console_output(self): def test_console_output(self):
"""Make sure we can get console output from instance""" """Make sure we can get console output from instance"""
instance_id = self._create_instance() instance_id = self._create_instance()

View File

@@ -19,6 +19,8 @@
Tests For Scheduler Tests For Scheduler
""" """
import datetime
from nova import context from nova import context
from nova import db from nova import db
from nova import flags from nova import flags
@@ -95,7 +97,7 @@ class SimpleDriverTestCase(test.TestCase):
self.manager.delete_user(self.user) self.manager.delete_user(self.user)
self.manager.delete_project(self.project) self.manager.delete_project(self.project)
def _create_instance(self): def _create_instance(self, **kwargs):
"""Create a test instance""" """Create a test instance"""
inst = {} inst = {}
inst['image_id'] = 'ami-test' inst['image_id'] = 'ami-test'
@@ -106,6 +108,7 @@ class SimpleDriverTestCase(test.TestCase):
inst['mac_address'] = utils.generate_mac() inst['mac_address'] = utils.generate_mac()
inst['ami_launch_index'] = 0 inst['ami_launch_index'] = 0
inst['vcpus'] = 1 inst['vcpus'] = 1
inst['availability_zone'] = kwargs.get('availability_zone', None)
return db.instance_create(self.context, inst)['id'] return db.instance_create(self.context, inst)['id']
def _create_volume(self): def _create_volume(self):
@@ -114,9 +117,33 @@ class SimpleDriverTestCase(test.TestCase):
vol['image_id'] = 'ami-test' vol['image_id'] = 'ami-test'
vol['reservation_id'] = 'r-fakeres' vol['reservation_id'] = 'r-fakeres'
vol['size'] = 1 vol['size'] = 1
vol['availability_zone'] = 'test'
return db.volume_create(self.context, vol)['id'] return db.volume_create(self.context, vol)['id']
def test_hosts_are_up(self): def test_doesnt_report_disabled_hosts_as_up(self):
"""Ensures driver doesn't find hosts before they are enabled"""
# NOTE(vish): constructing service without create method
# because we are going to use it without queue
compute1 = service.Service('host1',
'nova-compute',
'compute',
FLAGS.compute_manager)
compute1.start()
compute2 = service.Service('host2',
'nova-compute',
'compute',
FLAGS.compute_manager)
compute2.start()
s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute')
s2 = db.service_get_by_args(self.context, 'host2', 'nova-compute')
db.service_update(self.context, s1['id'], {'disabled': True})
db.service_update(self.context, s2['id'], {'disabled': True})
hosts = self.scheduler.driver.hosts_up(self.context, 'compute')
self.assertEqual(0, len(hosts))
compute1.kill()
compute2.kill()
def test_reports_enabled_hosts_as_up(self):
"""Ensures driver can find the hosts that are up""" """Ensures driver can find the hosts that are up"""
# NOTE(vish): constructing service without create method # NOTE(vish): constructing service without create method
# because we are going to use it without queue # because we are going to use it without queue
@@ -131,7 +158,7 @@ class SimpleDriverTestCase(test.TestCase):
FLAGS.compute_manager) FLAGS.compute_manager)
compute2.start() compute2.start()
hosts = self.scheduler.driver.hosts_up(self.context, 'compute') hosts = self.scheduler.driver.hosts_up(self.context, 'compute')
self.assertEqual(len(hosts), 2) self.assertEqual(2, len(hosts))
compute1.kill() compute1.kill()
compute2.kill() compute2.kill()
@@ -158,6 +185,63 @@ class SimpleDriverTestCase(test.TestCase):
compute1.kill() compute1.kill()
compute2.kill() compute2.kill()
def test_specific_host_gets_instance(self):
"""Ensures if you set availability_zone it launches on that zone"""
compute1 = service.Service('host1',
'nova-compute',
'compute',
FLAGS.compute_manager)
compute1.start()
compute2 = service.Service('host2',
'nova-compute',
'compute',
FLAGS.compute_manager)
compute2.start()
instance_id1 = self._create_instance()
compute1.run_instance(self.context, instance_id1)
instance_id2 = self._create_instance(availability_zone='nova:host1')
host = self.scheduler.driver.schedule_run_instance(self.context,
instance_id2)
self.assertEqual('host1', host)
compute1.terminate_instance(self.context, instance_id1)
db.instance_destroy(self.context, instance_id2)
compute1.kill()
compute2.kill()
def test_wont_sechedule_if_specified_host_is_down(self):
compute1 = service.Service('host1',
'nova-compute',
'compute',
FLAGS.compute_manager)
compute1.start()
s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute')
now = datetime.datetime.utcnow()
delta = datetime.timedelta(seconds=FLAGS.service_down_time * 2)
past = now - delta
db.service_update(self.context, s1['id'], {'updated_at': past})
instance_id2 = self._create_instance(availability_zone='nova:host1')
self.assertRaises(driver.WillNotSchedule,
self.scheduler.driver.schedule_run_instance,
self.context,
instance_id2)
db.instance_destroy(self.context, instance_id2)
compute1.kill()
def test_will_schedule_on_disabled_host_if_specified(self):
compute1 = service.Service('host1',
'nova-compute',
'compute',
FLAGS.compute_manager)
compute1.start()
s1 = db.service_get_by_args(self.context, 'host1', 'nova-compute')
db.service_update(self.context, s1['id'], {'disabled': True})
instance_id2 = self._create_instance(availability_zone='nova:host1')
host = self.scheduler.driver.schedule_run_instance(self.context,
instance_id2)
self.assertEqual('host1', host)
db.instance_destroy(self.context, instance_id2)
compute1.kill()
def test_too_many_cores(self): def test_too_many_cores(self):
"""Ensures we don't go over max cores""" """Ensures we don't go over max cores"""
compute1 = service.Service('host1', compute1 = service.Service('host1',

View File

@@ -29,9 +29,9 @@ from nova.auth import manager
from nova.compute import instance_types from nova.compute import instance_types
from nova.compute import power_state from nova.compute import power_state
from nova.virt import xenapi_conn from nova.virt import xenapi_conn
from nova.virt.xenapi import fake from nova.virt.xenapi import fake as xenapi_fake
from nova.virt.xenapi import volume_utils from nova.virt.xenapi import volume_utils
from nova.tests.db import fakes from nova.tests.db import fakes as db_fakes
from nova.tests.xenapi import stubs from nova.tests.xenapi import stubs
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@@ -47,9 +47,9 @@ class XenAPIVolumeTestCase(test.TestCase):
FLAGS.target_host = '127.0.0.1' FLAGS.target_host = '127.0.0.1'
FLAGS.xenapi_connection_url = 'test_url' FLAGS.xenapi_connection_url = 'test_url'
FLAGS.xenapi_connection_password = 'test_pass' FLAGS.xenapi_connection_password = 'test_pass'
fakes.stub_out_db_instance_api(self.stubs) db_fakes.stub_out_db_instance_api(self.stubs)
stubs.stub_out_get_target(self.stubs) stubs.stub_out_get_target(self.stubs)
fake.reset() xenapi_fake.reset()
self.values = {'name': 1, 'id': 1, self.values = {'name': 1, 'id': 1,
'project_id': 'fake', 'project_id': 'fake',
'user_id': 'fake', 'user_id': 'fake',
@@ -79,11 +79,11 @@ class XenAPIVolumeTestCase(test.TestCase):
helper = volume_utils.VolumeHelper helper = volume_utils.VolumeHelper
helper.XenAPI = session.get_imported_xenapi() helper.XenAPI = session.get_imported_xenapi()
vol = self._create_volume() vol = self._create_volume()
info = helper.parse_volume_info(vol['ec2_id'], '/dev/sdc') info = helper.parse_volume_info(vol['id'], '/dev/sdc')
label = 'SR-%s' % vol['ec2_id'] label = 'SR-%s' % vol['id']
description = 'Test-SR' description = 'Test-SR'
sr_ref = helper.create_iscsi_storage(session, info, label, description) sr_ref = helper.create_iscsi_storage(session, info, label, description)
srs = fake.get_all('SR') srs = xenapi_fake.get_all('SR')
self.assertEqual(sr_ref, srs[0]) self.assertEqual(sr_ref, srs[0])
db.volume_destroy(context.get_admin_context(), vol['id']) db.volume_destroy(context.get_admin_context(), vol['id'])
@@ -97,7 +97,7 @@ class XenAPIVolumeTestCase(test.TestCase):
# oops, wrong mount point! # oops, wrong mount point!
self.assertRaises(volume_utils.StorageError, self.assertRaises(volume_utils.StorageError,
helper.parse_volume_info, helper.parse_volume_info,
vol['ec2_id'], vol['id'],
'/dev/sd') '/dev/sd')
db.volume_destroy(context.get_admin_context(), vol['id']) db.volume_destroy(context.get_admin_context(), vol['id'])
@@ -107,17 +107,16 @@ class XenAPIVolumeTestCase(test.TestCase):
conn = xenapi_conn.get_connection(False) conn = xenapi_conn.get_connection(False)
volume = self._create_volume() volume = self._create_volume()
instance = db.instance_create(self.values) instance = db.instance_create(self.values)
fake.create_vm(instance.name, 'Running') xenapi_fake.create_vm(instance.name, 'Running')
result = conn.attach_volume(instance.name, volume['ec2_id'], result = conn.attach_volume(instance.name, volume['id'], '/dev/sdc')
'/dev/sdc')
def check(): def check():
# check that the VM has a VBD attached to it # check that the VM has a VBD attached to it
# Get XenAPI reference for the VM # Get XenAPI reference for the VM
vms = fake.get_all('VM') vms = xenapi_fake.get_all('VM')
# Get XenAPI record for VBD # Get XenAPI record for VBD
vbds = fake.get_all('VBD') vbds = xenapi_fake.get_all('VBD')
vbd = fake.get_record('VBD', vbds[0]) vbd = xenapi_fake.get_record('VBD', vbds[0])
vm_ref = vbd['VM'] vm_ref = vbd['VM']
self.assertEqual(vm_ref, vms[0]) self.assertEqual(vm_ref, vms[0])
@@ -130,11 +129,11 @@ class XenAPIVolumeTestCase(test.TestCase):
conn = xenapi_conn.get_connection(False) conn = xenapi_conn.get_connection(False)
volume = self._create_volume() volume = self._create_volume()
instance = db.instance_create(self.values) instance = db.instance_create(self.values)
fake.create_vm(instance.name, 'Running') xenapi_fake.create_vm(instance.name, 'Running')
self.assertRaises(Exception, self.assertRaises(Exception,
conn.attach_volume, conn.attach_volume,
instance.name, instance.name,
volume['ec2_id'], volume['id'],
'/dev/sdc') '/dev/sdc')
def tearDown(self): def tearDown(self):
@@ -156,41 +155,70 @@ class XenAPIVMTestCase(test.TestCase):
self.stubs = stubout.StubOutForTesting() self.stubs = stubout.StubOutForTesting()
FLAGS.xenapi_connection_url = 'test_url' FLAGS.xenapi_connection_url = 'test_url'
FLAGS.xenapi_connection_password = 'test_pass' FLAGS.xenapi_connection_password = 'test_pass'
fake.reset() xenapi_fake.reset()
fakes.stub_out_db_instance_api(self.stubs) db_fakes.stub_out_db_instance_api(self.stubs)
fake.create_network('fake', FLAGS.flat_network_bridge) xenapi_fake.create_network('fake', FLAGS.flat_network_bridge)
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
self.conn = xenapi_conn.get_connection(False)
def test_list_instances_0(self): def test_list_instances_0(self):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) instances = self.conn.list_instances()
conn = xenapi_conn.get_connection(False)
instances = conn.list_instances()
self.assertEquals(instances, []) self.assertEquals(instances, [])
def test_spawn(self): def test_get_diagnostics(self):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) instance = self._create_instance()
values = {'name': 1, 'id': 1, self.conn.get_diagnostics(instance)
'project_id': self.project.id,
'user_id': self.user.id, def test_instance_snapshot(self):
'image_id': 1, stubs.stubout_instance_snapshot(self.stubs)
'kernel_id': 2, instance = self._create_instance()
'ramdisk_id': 3,
'instance_type': 'm1.large', name = "MySnapshot"
'mac_address': 'aa:bb:cc:dd:ee:ff', template_vm_ref = self.conn.snapshot(instance, name)
}
conn = xenapi_conn.get_connection(False) def ensure_vm_was_torn_down():
instance = db.instance_create(values) vm_labels = []
conn.spawn(instance) for vm_ref in xenapi_fake.get_all('VM'):
vm_rec = xenapi_fake.get_record('VM', vm_ref)
if not vm_rec["is_control_domain"]:
vm_labels.append(vm_rec["name_label"])
self.assertEquals(vm_labels, [1])
def ensure_vbd_was_torn_down():
vbd_labels = []
for vbd_ref in xenapi_fake.get_all('VBD'):
vbd_rec = xenapi_fake.get_record('VBD', vbd_ref)
vbd_labels.append(vbd_rec["vm_name_label"])
self.assertEquals(vbd_labels, [1])
def ensure_vdi_was_torn_down():
for vdi_ref in xenapi_fake.get_all('VDI'):
vdi_rec = xenapi_fake.get_record('VDI', vdi_ref)
name_label = vdi_rec["name_label"]
self.assert_(not name_label.endswith('snapshot'))
def check(): def check():
instances = conn.list_instances() ensure_vm_was_torn_down()
ensure_vbd_was_torn_down()
ensure_vdi_was_torn_down()
check()
def test_spawn(self):
instance = self._create_instance()
def check():
instances = self.conn.list_instances()
self.assertEquals(instances, [1]) self.assertEquals(instances, [1])
# Get Nova record for VM # Get Nova record for VM
vm_info = conn.get_info(1) vm_info = self.conn.get_info(1)
# Get XenAPI record for VM # Get XenAPI record for VM
vms = fake.get_all('VM') vms = xenapi_fake.get_all('VM')
vm = fake.get_record('VM', vms[0]) vm = xenapi_fake.get_record('VM', vms[0])
# Check that m1.large above turned into the right thing. # Check that m1.large above turned into the right thing.
instance_type = instance_types.INSTANCE_TYPES['m1.large'] instance_type = instance_types.INSTANCE_TYPES['m1.large']
@@ -218,3 +246,19 @@ class XenAPIVMTestCase(test.TestCase):
self.manager.delete_project(self.project) self.manager.delete_project(self.project)
self.manager.delete_user(self.user) self.manager.delete_user(self.user)
self.stubs.UnsetAll() self.stubs.UnsetAll()
def _create_instance(self):
"""Creates and spawns a test instance"""
values = {
'name': 1,
'id': 1,
'project_id': self.project.id,
'user_id': self.user.id,
'image_id': 1,
'kernel_id': 2,
'ramdisk_id': 3,
'instance_type': 'm1.large',
'mac_address': 'aa:bb:cc:dd:ee:ff'}
instance = db.instance_create(values)
self.conn.spawn(instance)
return instance

View File

@@ -19,6 +19,54 @@
from nova.virt import xenapi_conn from nova.virt import xenapi_conn
from nova.virt.xenapi import fake from nova.virt.xenapi import fake
from nova.virt.xenapi import volume_utils from nova.virt.xenapi import volume_utils
from nova.virt.xenapi import vm_utils
def stubout_instance_snapshot(stubs):
@classmethod
def fake_fetch_image(cls, session, instance_id, image, user, project,
type):
# Stubout wait_for_task
def fake_wait_for_task(self, id, task):
class FakeEvent:
def send(self, value):
self.rv = value
def wait(self):
return self.rv
done = FakeEvent()
self._poll_task(id, task, done)
rv = done.wait()
return rv
stubs.Set(xenapi_conn.XenAPISession, 'wait_for_task',
fake_wait_for_task)
from nova.virt.xenapi.fake import create_vdi
name_label = "instance-%s" % instance_id
#TODO: create fake SR record
sr_ref = "fakesr"
vdi_ref = create_vdi(name_label=name_label, read_only=False,
sr_ref=sr_ref, sharable=False)
vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
vdi_uuid = vdi_rec['uuid']
return vdi_uuid
stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image)
def fake_parse_xmlrpc_value(val):
return val
stubs.Set(xenapi_conn, '_parse_xmlrpc_value', fake_parse_xmlrpc_value)
def fake_wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref,
original_parent_uuid):
#TODO(sirp): Should we actually fake out the data here
return "fakeparent"
stubs.Set(vm_utils, 'wait_for_vhd_coalesce', fake_wait_for_vhd_coalesce)
def stubout_session(stubs, cls): def stubout_session(stubs, cls):
@@ -63,6 +111,24 @@ class FakeSessionForVMTests(fake.SessionBase):
vm['is_a_template'] = False vm['is_a_template'] = False
vm['is_control_domain'] = False vm['is_control_domain'] = False
def VM_snapshot(self, session_ref, vm_ref, label):
status = "Running"
template_vm_ref = fake.create_vm(label, status, is_a_template=True,
is_control_domain=False)
sr_ref = "fakesr"
template_vdi_ref = fake.create_vdi(label, read_only=True,
sr_ref=sr_ref, sharable=False)
template_vbd_ref = fake.create_vbd(template_vm_ref, template_vdi_ref)
return template_vm_ref
def VDI_destroy(self, session_ref, vdi_ref):
fake.destroy_vdi(vdi_ref)
def VM_destroy(self, session_ref, vm_ref):
fake.destroy_vm(vm_ref)
class FakeSessionForVolumeTests(fake.SessionBase): class FakeSessionForVolumeTests(fake.SessionBase):
""" Stubs out a XenAPISession for Volume tests """ """ Stubs out a XenAPISession for Volume tests """

68
run_tests.py Normal file
View File

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