From 7c8096384507908a5e583f4554d0fc765ae5f2eb Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Thu, 27 Jan 2011 20:39:33 +0900
Subject: adding testcode
---
bin/nova-api | 2 +
nova/compute/manager.py | 66 ++--
nova/db/sqlalchemy/api.py | 49 +--
nova/db/sqlalchemy/models.py | 12 +-
nova/scheduler/driver.py | 53 ++--
nova/scheduler/manager.py | 28 +-
nova/tests/test_compute.py | 305 ++++++++++++++++++
nova/tests/test_scheduler.py | 722 +++++++++++++++++++++++++++++++++++++++++++
nova/tests/test_service.py | 61 +++-
nova/tests/test_virt.py | 520 ++++++++++++++++++++++++++++++-
nova/virt/fake.py | 12 +-
nova/virt/libvirt_conn.py | 159 +++++-----
nova/virt/xenapi_conn.py | 14 +-
nova/volume/manager.py | 2 +-
14 files changed, 1809 insertions(+), 196 deletions(-)
diff --git a/bin/nova-api b/bin/nova-api
index 7b4fbeab1..fba09889f 100755
--- a/bin/nova-api
+++ b/bin/nova-api
@@ -37,11 +37,13 @@ gettext.install('nova', unicode=1)
from nova import flags
from nova import log as logging
from nova import wsgi
+from nova import utils
logging.basicConfig()
LOG = logging.getLogger('nova.api')
LOG.setLevel(logging.DEBUG)
+utils.default_flagfile()
FLAGS = flags.FLAGS
API_ENDPOINTS = ['ec2', 'osapi']
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index efb5753aa..4acba7153 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -125,12 +125,12 @@ class ComputeManager(manager.Manager):
"""Insert compute node specific information to DB."""
try:
- service_ref = db.service_get_by_args(ctxt,
- host,
- binary)
+ service_ref = self.db.service_get_by_args(ctxt,
+ host,
+ binary)
except exception.NotFound:
- msg = _(("""Cannot insert compute manager specific info"""
- """Because no service record found."""))
+ msg = _(("""Cannot insert compute manager specific info,"""
+ """ Because no service record found."""))
raise exception.Invalid(msg)
# Updating host information
@@ -141,14 +141,14 @@ class ComputeManager(manager.Manager):
version = self.driver.get_hypervisor_version()
cpu_info = self.driver.get_cpu_info()
- db.service_update(ctxt,
- service_ref['id'],
- {'vcpus': vcpu,
- 'memory_mb': memory_mb,
- 'local_gb': local_gb,
- 'hypervisor_type': hypervisor,
- 'hypervisor_version': version,
- 'cpu_info': cpu_info})
+ self.db.service_update(ctxt,
+ service_ref['id'],
+ {'vcpus': vcpu,
+ 'memory_mb': memory_mb,
+ 'local_gb': local_gb,
+ 'hypervisor_type': hypervisor,
+ 'hypervisor_version': version,
+ 'cpu_info': cpu_info})
def _update_state(self, context, instance_id):
"""Update the state of an instance from the driver info."""
@@ -596,22 +596,22 @@ class ComputeManager(manager.Manager):
""" Check the host cpu is compatible to a cpu given by xml."""
return self.driver.compare_cpu(cpu_info)
- def pre_live_migration(self, context, instance_id, dest):
+ def pre_live_migration(self, context, instance_id):
"""Any preparation for live migration at dst host."""
# Getting instance info
- instance_ref = db.instance_get(context, instance_id)
+ instance_ref = self.db.instance_get(context, instance_id)
ec2_id = instance_ref['hostname']
# Getting fixed ips
- fixed_ip = db.instance_get_fixed_address(context, instance_id)
+ fixed_ip = self.db.instance_get_fixed_address(context, instance_id)
if not fixed_ip:
msg = _('%s(%s) doesnt have fixed_ip') % (instance_id, ec2_id)
raise exception.NotFound(msg)
# If any volume is mounted, prepare here.
if len(instance_ref['volumes']) == 0:
- logging.info(_("%s has no volume.") % ec2_id)
+ LOG.info(_("%s has no volume."), ec2_id)
else:
for v in instance_ref['volumes']:
self.volume_manager.setup_compute_volume(context, v['id'])
@@ -634,7 +634,7 @@ class ComputeManager(manager.Manager):
"""executes live migration."""
# Get instance for error handling.
- instance_ref = db.instance_get(context, instance_id)
+ instance_ref = self.db.instance_get(context, instance_id)
ec2_id = instance_ref['hostname']
try:
@@ -647,27 +647,27 @@ class ComputeManager(manager.Manager):
"args": {'instance_id': instance_id}})
# Asking dest host to preparing live migration.
- compute_topic = db.queue_get_for(context,
- FLAGS.compute_topic,
- dest)
+ compute_topic = self.db.queue_get_for(context,
+ FLAGS.compute_topic,
+ dest)
rpc.call(context,
- compute_topic,
- {"method": "pre_live_migration",
- "args": {'instance_id': instance_id,
- 'dest': dest}})
+ compute_topic,
+ {"method": "pre_live_migration",
+ "args": {'instance_id': instance_id}})
except Exception, e:
+ print e
msg = _('Pre live migration for %s failed at %s')
- logging.error(msg, ec2_id, dest)
- db.instance_set_state(context,
- instance_id,
- power_state.RUNNING,
- 'running')
+ LOG.error(msg, ec2_id, dest)
+ self.db.instance_set_state(context,
+ instance_id,
+ power_state.RUNNING,
+ 'running')
for v in instance_ref['volumes']:
- db.volume_update(context,
- v['id'],
- {'status': 'in-use'})
+ self.db.volume_update(context,
+ v['id'],
+ {'status': 'in-use'})
# e should be raised. just calling "raise" may raise NotFound.
raise e
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 248a46f65..1cdd5a286 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -897,41 +897,42 @@ def instance_get_all_by_host(context, hostname):
@require_context
-def _instance_get_sum_by_host_and_project(context, column, hostname, proj_id):
+def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id):
session = get_session()
-
result = session.query(models.Instance).\
- filter_by(host=hostname).\
- filter_by(project_id=proj_id).\
- filter_by(deleted=can_read_deleted(context)).\
- value(column)
- if not result:
+ filter_by(host=hostname).\
+ filter_by(project_id=proj_id).\
+ filter_by(deleted=False).\
+ value(func.sum(models.Instance.vcpus))
+ if None == result:
return 0
return result
-@require_context
-def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id):
- return _instance_get_sum_by_host_and_project(context,
- 'vcpus',
- hostname,
- proj_id)
-
-
@require_context
def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id):
- return _instance_get_sum_by_host_and_project(context,
- 'memory_mb',
- hostname,
- proj_id)
-
+ session = get_session()
+ result = session.query(models.Instance).\
+ filter_by(host=hostname).\
+ filter_by(project_id=proj_id).\
+ filter_by(deleted=False).\
+ value(func.sum(models.Instance.memory_mb))
+ if None == result:
+ return 0
+ return result
@require_context
def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id):
- return _instance_get_sum_by_host_and_project(context,
- 'local_gb',
- hostname,
- proj_id)
+ session = get_session()
+ result = session.query(models.Instance).\
+ filter_by(host=hostname).\
+ filter_by(project_id=proj_id).\
+ filter_by(deleted=False).\
+ value(func.sum(models.Instance.local_gb))
+ if None == result:
+ return 0
+ return result
+
@require_context
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index b28c64b59..7c40d5596 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -161,11 +161,11 @@ class Service(BASE, NovaBase):
# The below items are compute node only.
# -1 or None is inserted for other service.
- vcpus = Column(Integer, nullable=False, default=-1)
- memory_mb = Column(Integer, nullable=False, default=-1)
- local_gb = Column(Integer, nullable=False, default=-1)
- hypervisor_type = Column(String(128))
- hypervisor_version = Column(Integer, nullable=False, default=-1)
+ vcpus = Column(Integer, nullable=True)
+ memory_mb = Column(Integer, nullable=True)
+ local_gb = Column(Integer, nullable=True)
+ hypervisor_type = Column(String(128), nullable=True)
+ hypervisor_version = Column(Integer, nullable=True)
# Note(masumotok): Expected Strings example:
#
# '{"arch":"x86_64", "model":"Nehalem",
@@ -174,7 +174,7 @@ class Service(BASE, NovaBase):
#
# Points are "json translatable" and it must have all
# dictionary keys above.
- cpu_info = Column(String(512))
+ cpu_info = Column(Text(), nullable=True)
class Certificate(BASE, NovaBase):
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 65745093b..d4ad42388 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -69,11 +69,10 @@ class Scheduler(object):
raise NotImplementedError(_("Must implement a fallback schedule"))
def schedule_live_migration(self, context, instance_id, dest):
- """ live migration method """
+ """live migration method"""
# Whether instance exists and running
instance_ref = db.instance_get(context, instance_id)
- ec2_id = instance_ref['hostname']
# Checking instance.
self._live_migration_src_check(context, instance_ref)
@@ -159,48 +158,45 @@ class Scheduler(object):
def _live_migration_common_check(self, context, instance_ref, dest):
"""
- Live migration check routine.
- Below pre-checkings are followed by
- http://wiki.libvirt.org/page/TodoPreMigrationChecks
+ Live migration check routine.
+ Below pre-checkings are followed by
+ http://wiki.libvirt.org/page/TodoPreMigrationChecks
"""
# Checking dest exists.
dservice_refs = db.service_get_all_by_host(context, dest)
if len(dservice_refs) <= 0:
- msg = _('%s does not exists.')
- raise exception.Invalid(msg % dest)
+ raise exception.Invalid(_('%s does not exists.') % dest)
dservice_ref = dservice_refs[0]
# Checking original host( where instance was launched at) exists.
- orighost = instance_ref['launched_on']
- oservice_refs = db.service_get_all_by_host(context, orighost)
+ oservice_refs = db.service_get_all_by_host(context,
+ instance_ref['launched_on'])
if len(oservice_refs) <= 0:
msg = _('%s(where instance was launched at) does not exists.')
- raise exception.Invalid(msg % orighost)
+ raise exception.Invalid(msg % instance_ref['launched_on'])
oservice_ref = oservice_refs[0]
# Checking hypervisor is same.
- otype = oservice_ref['hypervisor_type']
- dtype = dservice_ref['hypervisor_type']
- if otype != dtype:
+ if oservice_ref['hypervisor_type'] != dservice_ref['hypervisor_type']:
msg = _('Different hypervisor type(%s->%s)')
- raise exception.Invalid(msg % (otype, dtype))
+ raise exception.Invalid(msg % (oservice_ref['hypervisor_type'],
+ dservice_ref['hypervisor_type']))
# Checkng hypervisor version.
- oversion = oservice_ref['hypervisor_version']
- dversion = dservice_ref['hypervisor_version']
- if oversion > dversion:
+ if oservice_ref['hypervisor_version'] > \
+ dservice_ref['hypervisor_version']:
msg = _('Older hypervisor version(%s->%s)')
- raise exception.Invalid(msg % (oversion, dversion))
+ raise exception.Invalid(msg % (oservice_ref['hypervisor_version'],
+ dservice_ref['hypervisor_version']))
# Checking cpuinfo.
- cpu_info = oservice_ref['cpu_info']
try:
rpc.call(context,
db.queue_get_for(context, FLAGS.compute_topic, dest),
{"method": 'compare_cpu',
- "args": {'cpu_info': cpu_info}})
+ "args": {'cpu_info': oservice_ref['cpu_info']}})
except rpc.RemoteError, e:
msg = _(("""%s doesnt have compatibility to %s"""
@@ -211,7 +207,7 @@ class Scheduler(object):
raise e
def has_enough_resource(self, context, instance_ref, dest):
- """ Check if destination host has enough resource for live migration"""
+ """Check if destination host has enough resource for live migration"""
# Getting instance information
ec2_id = instance_ref['hostname']
@@ -222,28 +218,27 @@ class Scheduler(object):
# Gettin host information
service_refs = db.service_get_all_by_host(context, dest)
if len(service_refs) <= 0:
- msg = _('%s does not exists.')
- raise exception.Invalid(msg % dest)
+ raise exception.Invalid(_('%s does not exists.') % dest)
service_ref = service_refs[0]
total_cpu = int(service_ref['vcpus'])
total_mem = int(service_ref['memory_mb'])
total_hdd = int(service_ref['local_gb'])
- instances_ref = db.instance_get_all_by_host(context, dest)
- for i_ref in instances_ref:
+ instances_refs = db.instance_get_all_by_host(context, dest)
+ for i_ref in instances_refs:
total_cpu -= int(i_ref['vcpus'])
total_mem -= int(i_ref['memory_mb'])
total_hdd -= int(i_ref['local_gb'])
# Checking host has enough information
- logging.debug('host(%s) remains vcpu:%s mem:%s hdd:%s,' %
+ logging.debug(_('host(%s) remains vcpu:%s mem:%s hdd:%s,') %
(dest, total_cpu, total_mem, total_hdd))
- logging.debug('instance(%s) has vcpu:%s mem:%s hdd:%s,' %
+ logging.debug(_('instance(%s) has vcpu:%s mem:%s hdd:%s,') %
(ec2_id, vcpus, mem, hdd))
if total_cpu <= vcpus or total_mem <= mem or total_hdd <= hdd:
- msg = '%s doesnt have enough resource for %s' % (dest, ec2_id)
- raise exception.NotEmpty(msg)
+ raise exception.NotEmpty(_('%s is not capable to migrate %s') %
+ (dest, ec2_id))
logging.debug(_('%s has_enough_resource() for %s') % (dest, ec2_id))
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index 1cc767a03..a181225a6 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -73,17 +73,13 @@ class SchedulerManager(manager.Manager):
# Based on bear design summit discussion,
# just put this here for bexar release.
def show_host_resource(self, context, host, *args):
- """ show the physical/usage resource given by hosts."""
+ """show the physical/usage resource given by hosts."""
- services = db.service_get_all_by_host(context, host)
- if len(services) == 0:
- return {'ret': False, 'msg': 'No such Host'}
-
- compute = [s for s in services if s['topic'] == 'compute']
- if 0 == len(compute):
- service_ref = services[0]
- else:
- service_ref = compute[0]
+ computes = db.service_get_all_compute_sorted(context)
+ computes = [s for s,v in computes if s['host'] == host]
+ if 0 == len(computes):
+ return {'ret': False, 'msg': 'No such Host or not compute node.'}
+ service_ref = computes[0]
# Getting physical resource information
h_resource = {'vcpus': service_ref['vcpus'],
@@ -92,13 +88,15 @@ class SchedulerManager(manager.Manager):
# Getting usage resource information
u_resource = {}
- instances_ref = db.instance_get_all_by_host(context,
- service_ref['host'])
+ instances_refs = db.instance_get_all_by_host(context,
+ service_ref['host'])
- if 0 == len(instances_ref):
- return {'ret': True, 'phy_resource': h_resource, 'usage': {}}
+ if 0 == len(instances_refs):
+ return {'ret': True,
+ 'phy_resource': h_resource,
+ 'usage': u_resource}
- project_ids = [i['project_id'] for i in instances_ref]
+ project_ids = [i['project_id'] for i in instances_refs]
project_ids = list(set(project_ids))
for p_id in project_ids:
vcpus = db.instance_get_vcpu_sum_by_host_and_project(context,
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 09f6ee94a..344c2d2b5 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -20,6 +20,7 @@ Tests For Compute
"""
import datetime
+import mox
from nova import compute
from nova import context
@@ -27,9 +28,12 @@ from nova import db
from nova import exception
from nova import flags
from nova import log as logging
+from nova import rpc
from nova import test
from nova import utils
from nova.auth import manager
+from nova.compute import manager as compute_manager
+from nova.compute import power_state
LOG = logging.getLogger('nova.tests.compute')
@@ -219,3 +223,304 @@ class ComputeTestCase(test.TestCase):
self.assertEqual(ret_val, None)
self.compute.terminate_instance(self.context, instance_id)
+
+ def test_update_service_exception(self):
+ """nova-compute updates Serivce table on DB like below.
+ nova.service.Serivce.start ->
+ nova.compute.ComputeManager.update_service.
+ This testcase confirms if no record found on Service
+ table, exception can be raised.
+ """
+ host = 'foo'
+ binary = 'nova-compute'
+ dbmock = self.mox.CreateMock(db)
+ dbmock.service_get_by_args(mox.IgnoreArg(),
+ mox.StrContains(host),
+ mox.StrContains(binary)).\
+ AndRaise(exception.NotFound())
+ self.compute.db = dbmock
+ self.mox.ReplayAll()
+ try:
+ self.compute.update_service('dummy', host, binary)
+ except exception.Invalid, e:
+ msg = 'Cannot insert compute manager specific info'
+ c1 = ( 0 <= e.message.find(msg))
+ self.assertTrue(c1)
+ self.mox.ResetAll()
+
+ def test_update_service_success(self):
+ """nova-compute updates Serivce table on DB like below.
+ nova.service.Serivce.start ->
+ nova.compute.ComputeManager.update_service.
+ In this method, vcpus/memory_mb/local_gb/hypervisor_type/
+ hypervisor_version/cpu_info should be changed.
+ Based on this specification, this testcase confirms
+ if this method finishes successfully,
+ meaning self.db.service_update is called with dictinary
+
+ {'vcpu':aaa, 'memory_mb':bbb, 'local_gb':ccc,
+ 'hypervisor_type':ddd, 'hypervisor_version':eee,
+ 'cpu_info':fff}
+
+ Since each value of above dict can be obtained through
+ driver(different depends on environment),
+ only dictionary keys are checked.
+ """
+
+ def dic_key_check(dic):
+ validkey = ['vcpus', 'memory_mb', 'local_gb',
+ 'hypervisor_type', 'hypervisor_version', 'cpu_info']
+ return (list(set(validkey)) == list(set(dic.keys())))
+
+ host = 'foo'
+ binary = 'nova-compute'
+ service_ref = {'id':1, 'binary':'nova-compute', 'topic':'compute'}
+ dbmock = self.mox.CreateMock(db)
+ dbmock.service_get_by_args(mox.IgnoreArg(),
+ mox.StrContains(host),
+ mox.StrContains(binary)).\
+ AndReturn(service_ref)
+ dbmock.service_update(mox.IgnoreArg(),
+ service_ref['id'],
+ mox.Func(dic_key_check))
+
+ self.compute.db = dbmock
+ self.mox.ReplayAll()
+ try:
+ self.compute.update_service('dummy', host, binary)
+ except exception.Invalid, e:
+ msg = 'Cannot insert compute manager specific info'
+ c1 = ( 0 <= e.message.find(msg))
+ self.assertTrue(c1)
+ self.mox.ResetAll()
+
+ def _setup_other_managers(self):
+ self.volume_manager = utils.import_object(FLAGS.volume_manager)
+ self.network_manager = utils.import_object(FLAGS.network_manager)
+ self.compute_driver = utils.import_object(FLAGS.compute_driver)
+
+ def test_pre_live_migration_instance_has_no_fixed_ip(self):
+ """
+ if instances that are intended to be migrated doesnt have fixed_ip
+ (not happens usually), pre_live_migration has to raise Exception.
+ """
+ instance_ref={'id':1, 'volumes':[{'id':1}, {'id':2}],
+ 'hostname':'i-000000001'}
+ c = context.get_admin_context()
+ i_id = instance_ref['id']
+
+ dbmock = self.mox.CreateMock(db)
+ dbmock.instance_get(c, i_id).AndReturn(instance_ref)
+ dbmock.instance_get_fixed_address(c, i_id).AndReturn(None)
+
+ self.compute.db = dbmock
+ self.mox.ReplayAll()
+ self.assertRaises(exception.NotFound,
+ self.compute.pre_live_migration,
+ c, instance_ref['id'])
+ self.mox.ResetAll()
+
+ def test_pre_live_migration_instance_has_volume(self):
+ """if any volumes are attached to the instances that are
+ intended to be migrated, setup_compute_volume must be
+ called because aoe module should be inserted at destination
+ host. This testcase checks on it.
+ """
+ instance_ref={'id':1, 'volumes':[{'id':1}, {'id':2}],
+ 'hostname':'i-000000001'}
+ c = context.get_admin_context()
+ i_id=instance_ref['id']
+
+ self._setup_other_managers()
+ dbmock = self.mox.CreateMock(db)
+ volmock = self.mox.CreateMock(self.volume_manager)
+ netmock = self.mox.CreateMock(self.network_manager)
+ drivermock = self.mox.CreateMock(self.compute_driver)
+
+ dbmock.instance_get(c, i_id).AndReturn(instance_ref)
+ dbmock.instance_get_fixed_address(c, i_id).AndReturn('dummy')
+ for i in range(len(instance_ref['volumes'])):
+ vid = instance_ref['volumes'][i]['id']
+ volmock.setup_compute_volume(c, vid).InAnyOrder('g1')
+ netmock.setup_compute_network(c, instance_ref['id'])
+ drivermock.ensure_filtering_rules_for_instance(instance_ref)
+
+ self.compute.db = dbmock
+ self.compute.volume_manager = volmock
+ self.compute.network_manager = netmock
+ self.compute.driver = drivermock
+
+ self.mox.ReplayAll()
+ ret = self.compute.pre_live_migration(c, i_id)
+ self.assertEqual(ret, None)
+ self.mox.ResetAll()
+
+ def test_pre_live_migration_instance_has_no_volume(self):
+ """if any volumes are not attached to the instances that are
+ intended to be migrated, log message should be appears
+ because administrator can proove instance conditions before
+ live_migration if any trouble occurs.
+ """
+ instance_ref={'id':1, 'volumes':[], 'hostname':'i-20000001'}
+ c = context.get_admin_context()
+ i_id = instance_ref['id']
+
+ self._setup_other_managers()
+ dbmock = self.mox.CreateMock(db)
+ netmock = self.mox.CreateMock(self.network_manager)
+ drivermock = self.mox.CreateMock(self.compute_driver)
+
+ dbmock.instance_get(c, i_id).AndReturn(instance_ref)
+ dbmock.instance_get_fixed_address(c, i_id).AndReturn('dummy')
+ self.mox.StubOutWithMock(compute_manager.LOG, 'info')
+ compute_manager.LOG.info(_("%s has no volume."), instance_ref['hostname'])
+ netmock.setup_compute_network(c, i_id)
+ drivermock.ensure_filtering_rules_for_instance(instance_ref)
+
+ self.compute.db = dbmock
+ self.compute.network_manager = netmock
+ self.compute.driver = drivermock
+
+ self.mox.ReplayAll()
+ ret = self.compute.pre_live_migration(c, i_id)
+ self.assertEqual(ret, None)
+ self.mox.ResetAll()
+
+ def test_live_migration_instance_has_volume(self):
+ """Any volumes are mounted by instances to be migrated are found,
+ vblade health must be checked before starting live-migration.
+ And that is checked by check_for_export().
+ This testcase confirms check_for_export() is called.
+ """
+ instance_ref={'id':1, 'volumes':[{'id':1}, {'id':2}], 'hostname':'i-00000001'}
+ c = context.get_admin_context()
+ dest='dummydest'
+ i_id = instance_ref['id']
+
+ self._setup_other_managers()
+ dbmock = self.mox.CreateMock(db)
+ drivermock = self.mox.CreateMock(self.compute_driver)
+
+ dbmock.instance_get(c, instance_ref['id']).AndReturn(instance_ref)
+ self.mox.StubOutWithMock(rpc, 'call')
+ rpc.call(c, FLAGS.volume_topic,
+ {"method": "check_for_export",
+ "args": {'instance_id': i_id}}).InAnyOrder('g1')
+ rpc.call(c, db.queue_get_for(c, FLAGS.compute_topic, dest),
+ {"method": "pre_live_migration",
+ "args": {'instance_id': i_id}}).InAnyOrder('g1')
+
+ self.compute.db = dbmock
+ self.compute.driver = drivermock
+ self.mox.ReplayAll()
+ ret = self.compute.live_migration(c, i_id, dest)
+ self.assertEqual(ret, None)
+ self.mox.ResetAll()
+
+ def test_live_migration_instance_has_volume_and_exception(self):
+ """In addition to test_live_migration_instance_has_volume testcase,
+ this testcase confirms if any exception raises from check_for_export().
+ Then, valid seaquence of this method should recovering instance/volumes
+ status(ex. instance['state_description'] is changed from 'migrating'
+ -> 'running', was changed by scheduler)
+ """
+ instance_ref={'id':1, 'volumes':[{'id':1}, {'id':2}],
+ 'hostname':'i-000000001'}
+ dest='dummydest'
+ c = context.get_admin_context()
+ i_id = instance_ref['id']
+
+ self._setup_other_managers()
+ dbmock = self.mox.CreateMock(db)
+ drivermock = self.mox.CreateMock(self.compute_driver)
+
+ dbmock.instance_get(c, instance_ref['id']).AndReturn(instance_ref)
+ self.mox.StubOutWithMock(rpc, 'call')
+ rpc.call(c, FLAGS.volume_topic,
+ {"method": "check_for_export",
+ "args": {'instance_id': i_id}}).InAnyOrder('g1')
+ compute_topic = db.queue_get_for(c, FLAGS.compute_topic, dest)
+ dbmock.queue_get_for(c, FLAGS.compute_topic, dest).AndReturn(compute_topic)
+ rpc.call(c, db.queue_get_for(c, FLAGS.compute_topic, dest),
+ {"method": "pre_live_migration",
+ "args": {'instance_id': i_id}}).\
+ InAnyOrder('g1').AndRaise(rpc.RemoteError('du', 'mm', 'y'))
+ self.mox.StubOutWithMock(compute_manager.LOG, 'error')
+ compute_manager.LOG.error('Pre live migration for %s failed at %s',
+ instance_ref['hostname'], dest)
+ dbmock.instance_set_state(c, i_id, power_state.RUNNING, 'running')
+ for i in range(len(instance_ref['volumes'])):
+ vid = instance_ref['volumes'][i]['id']
+ dbmock.volume_update(c, vid, {'status': 'in-use'})
+
+ self.compute.db = dbmock
+ self.compute.driver = drivermock
+ self.mox.ReplayAll()
+ self.assertRaises(rpc.RemoteError,
+ self.compute.live_migration,
+ c, i_id, dest)
+ self.mox.ResetAll()
+
+ def test_live_migration_instance_has_no_volume_and_exception(self):
+ """Simpler than test_live_migration_instance_has_volume_and_exception"""
+
+ instance_ref={'id':1, 'volumes':[], 'hostname':'i-000000001'}
+ dest='dummydest'
+ c = context.get_admin_context()
+ i_id = instance_ref['id']
+
+ self._setup_other_managers()
+ dbmock = self.mox.CreateMock(db)
+ drivermock = self.mox.CreateMock(self.compute_driver)
+
+ dbmock.instance_get(c, instance_ref['id']).AndReturn(instance_ref)
+ self.mox.StubOutWithMock(rpc, 'call')
+ compute_topic = db.queue_get_for(c, FLAGS.compute_topic, dest)
+ dbmock.queue_get_for(c, FLAGS.compute_topic, dest).AndReturn(compute_topic)
+ rpc.call(c, compute_topic,
+ {"method": "pre_live_migration",
+ "args": {'instance_id': i_id}}).\
+ AndRaise(rpc.RemoteError('du', 'mm', 'y'))
+ self.mox.StubOutWithMock(compute_manager.LOG, 'error')
+ compute_manager.LOG.error('Pre live migration for %s failed at %s',
+ instance_ref['hostname'], dest)
+ dbmock.instance_set_state(c, i_id, power_state.RUNNING, 'running')
+
+ self.compute.db = dbmock
+ self.compute.driver = drivermock
+ self.mox.ReplayAll()
+ self.assertRaises(rpc.RemoteError,
+ self.compute.live_migration,
+ c, i_id, dest)
+ self.mox.ResetAll()
+
+ def test_live_migration_instance_has_volume(self):
+ """Simpler version than test_live_migration_instance_has_volume."""
+ instance_ref={'id':1, 'volumes':[{'id':1}, {'id':2}],
+ 'hostname':'i-000000001'}
+ c = context.get_admin_context()
+ dest='dummydest'
+ i_id = instance_ref['id']
+
+ self._setup_other_managers()
+ dbmock = self.mox.CreateMock(db)
+ drivermock = self.mox.CreateMock(self.compute_driver)
+
+ dbmock.instance_get(c, i_id).AndReturn(instance_ref)
+ self.mox.StubOutWithMock(rpc, 'call')
+ rpc.call(c, FLAGS.volume_topic,
+ {"method": "check_for_export",
+ "args": {'instance_id': i_id}}).InAnyOrder('g1')
+ compute_topic = db.queue_get_for(c, FLAGS.compute_topic, dest)
+ dbmock.queue_get_for(c, FLAGS.compute_topic, dest).AndReturn(compute_topic)
+ rpc.call(c, compute_topic,
+ {"method": "pre_live_migration",
+ "args": {'instance_id': i_id}}).InAnyOrder('g1')
+ drivermock.live_migration(c, instance_ref, dest)
+
+ self.compute.db = dbmock
+ self.compute.driver = drivermock
+ self.mox.ReplayAll()
+ ret = self.compute.live_migration(c, i_id, dest)
+ self.assertEqual(ret, None)
+ self.mox.ResetAll()
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index 9d458244b..c62bca9b1 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -20,10 +20,12 @@ Tests For Scheduler
"""
import datetime
+import mox
from mox import IgnoreArg
from nova import context
from nova import db
+from nova import exception
from nova import flags
from nova import service
from nova import test
@@ -32,6 +34,8 @@ from nova import utils
from nova.auth import manager as auth_manager
from nova.scheduler import manager
from nova.scheduler import driver
+from nova.compute import power_state
+from nova.db.sqlalchemy import models
FLAGS = flags.FLAGS
@@ -75,7 +79,102 @@ class SchedulerTestCase(test.TestCase):
'args': {'num': 7}})
self.mox.ReplayAll()
scheduler.named_method(ctxt, 'topic', num=7)
+
+ def test_show_host_resource_host_not_exit(self):
+ """
+ A testcase of driver.has_enough_resource
+ given host does not exists.
+ """
+ scheduler = manager.SchedulerManager()
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+
+ self.mox.StubOutWithMock(manager, 'db', use_mock_anything=True)
+ manager.db.service_get_all_compute_sorted(mox.IgnoreArg()).\
+ AndReturn([])
+
+ self.mox.ReplayAll()
+ result = scheduler.show_host_resource(ctxt, dest)
+ # ret should be dict
+ keys = ['ret', 'msg']
+ c1 = list(set(result.keys())) == list(set(keys))
+ c2 = not result['ret']
+ c3 = result['msg'].find('No such Host or not compute node') <= 0
+ self.assertTrue( c1 and c2 and c3)
+ self.mox.UnsetStubs()
+
+ def test_show_host_resource_no_project(self):
+ """
+ A testcase of driver.show_host_resource
+ no instance stays on the given host
+ """
+ scheduler = manager.SchedulerManager()
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ r0 = {'vcpus':16, 'memory_mb':32, 'local_gb':100}
+ service_ref = {'id':1, 'host':dest}
+ service_ref.update(r0)
+
+ self.mox.StubOutWithMock(manager, 'db', use_mock_anything=True)
+ manager.db.service_get_all_compute_sorted(mox.IgnoreArg()).\
+ AndReturn([(service_ref, 0)])
+ manager.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([])
+
+ self.mox.ReplayAll()
+ result = scheduler.show_host_resource(ctxt, dest)
+ # ret should be dict
+ keys = ['ret', 'phy_resource', 'usage']
+ c1 = list(set(result.keys())) == list(set(keys))
+ c2 = result['ret']
+ c3 = result['phy_resource'] == r0
+ c4 = result['usage'] == {}
+ self.assertTrue( c1 and c2 and c3 and c4)
+ self.mox.UnsetStubs()
+
+ def test_show_host_resource_works_correctly(self):
+ """
+ A testcase of driver.show_host_resource
+ to make sure everything finished with no error.
+ """
+ scheduler = manager.SchedulerManager()
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ r0 = {'vcpus':16, 'memory_mb':32, 'local_gb':100}
+ r1 = {'vcpus':10, 'memory_mb':4, 'local_gb':20}
+ r2 = {'vcpus':10, 'memory_mb':20, 'local_gb':30}
+ service_ref = {'id':1, 'host':dest}
+ service_ref.update(r0)
+ instance_ref2 = {'id':2, 'project_id':'p-01', 'host':'dummy'}
+ instance_ref2.update(r1)
+ instance_ref3 = {'id':3, 'project_id':'p-02', 'host':'dummy'}
+ instance_ref3.update(r1)
+ self.mox.StubOutWithMock(manager, 'db', use_mock_anything=True)
+ manager.db.service_get_all_compute_sorted(mox.IgnoreArg()).\
+ AndReturn([(service_ref, 0)])
+ manager.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([instance_ref2, instance_ref3])
+ for p in ['p-01', 'p-02']:
+ manager.db.instance_get_vcpu_sum_by_host_and_project(
+ ctxt, dest, p).AndReturn(r2['vcpus'])
+ manager.db.instance_get_memory_sum_by_host_and_project(
+ ctxt, dest, p).AndReturn(r2['memory_mb'])
+ manager.db.instance_get_disk_sum_by_host_and_project(
+ ctxt, dest, p).AndReturn(r2['local_gb'])
+
+ self.mox.ReplayAll()
+ result = scheduler.show_host_resource(ctxt, dest)
+ # ret should be dict
+ keys = ['ret', 'phy_resource', 'usage']
+ c1 = list(set(result.keys())) == list(set(keys))
+ c2 = result['ret']
+ c3 = result['phy_resource'] == r0
+ c4 = result['usage'].keys() == ['p-01', 'p-02']
+ c5 = result['usage']['p-01'] == r2
+ c6 = result['usage']['p-02'] == r2
+ self.assertTrue( c1 and c2 and c3 and c4 and c5 and c6)
+ self.mox.UnsetStubs()
class ZoneSchedulerTestCase(test.TestCase):
"""Test case for zone scheduler"""
@@ -384,3 +483,626 @@ class SimpleDriverTestCase(test.TestCase):
volume2.delete_volume(self.context, volume_id)
volume1.kill()
volume2.kill()
+
+ def test_scheduler_live_migraiton_with_volume(self):
+ """
+ driver.scheduler_live_migration finishes successfully
+ (volumes are attached to instances)
+ This testcase make sure schedule_live_migration
+ changes instance state from 'running' -> 'migrating'
+ """
+ driver_i = self.scheduler.driver
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-00000001', 'host':'dummy',
+ 'volumes':[{'id':1}, {'id':2}]}
+ dest = 'dummydest'
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ # must be IgnoreArg() because scheduler changes ctxt's memory address
+ driver.db.instance_get(mox.IgnoreArg(), i_ref['id']).AndReturn(i_ref)
+
+ self.mox.StubOutWithMock(driver_i, '_live_migration_src_check')
+ driver_i._live_migration_src_check(mox.IgnoreArg(), i_ref)
+ self.mox.StubOutWithMock(driver_i, '_live_migration_dest_check')
+ driver_i._live_migration_dest_check(mox.IgnoreArg(), i_ref, dest)
+ self.mox.StubOutWithMock(driver_i, '_live_migration_common_check')
+ driver_i._live_migration_common_check(mox.IgnoreArg(), i_ref, dest)
+ driver.db.instance_set_state(mox.IgnoreArg(), i_ref['id'],
+ power_state.PAUSED, 'migrating')
+ for v in i_ref['volumes']:
+ driver.db.volume_update(mox.IgnoreArg(), v['id'],
+ {'status': 'migrating'})
+ self.mox.StubOutWithMock(rpc, 'cast', use_mock_anything=True)
+ kwargs={'instance_id':i_ref['id'], 'dest':dest}
+ rpc.cast(ctxt, db.queue_get_for(ctxt, topic, i_ref['host']),
+ {"method": 'live_migration',
+ "args": kwargs})
+
+ self.mox.ReplayAll()
+ self.scheduler.live_migration(ctxt, topic,
+ instance_id=i_ref['id'], dest=dest)
+ self.mox.UnsetStubs()
+
+ def test_scheduler_live_migraiton_no_volume(self):
+ """
+ driver.scheduler_live_migration finishes successfully
+ (volumes are attached to instances)
+ This testcase make sure schedule_live_migration
+ changes instance state from 'running' -> 'migrating'
+ """
+ driver_i = self.scheduler.driver
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy', 'volumes':[]}
+ dest = 'dummydest'
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ # must be IgnoreArg() because scheduler changes ctxt's memory address
+ driver.db.instance_get(mox.IgnoreArg(), i_ref['id']).AndReturn(i_ref)
+ self.mox.StubOutWithMock(driver_i, '_live_migration_src_check')
+ driver_i._live_migration_src_check(mox.IgnoreArg(), i_ref)
+ self.mox.StubOutWithMock(driver_i, '_live_migration_dest_check')
+ driver_i._live_migration_dest_check(mox.IgnoreArg(), i_ref, dest)
+ self.mox.StubOutWithMock(driver_i, '_live_migration_common_check')
+ driver_i._live_migration_common_check(mox.IgnoreArg(), i_ref, dest)
+ driver.db.instance_set_state(mox.IgnoreArg(), i_ref['id'],
+ power_state.PAUSED, 'migrating')
+ self.mox.StubOutWithMock(rpc, 'cast', use_mock_anything=True)
+ kwargs={'instance_id':i_ref['id'], 'dest':dest}
+ rpc.cast(ctxt, db.queue_get_for(ctxt, topic, i_ref['host']),
+ {"method": 'live_migration',
+ "args": kwargs})
+
+ self.mox.ReplayAll()
+ self.scheduler.live_migration(ctxt, topic,
+ instance_id=i_ref['id'], dest=dest)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_src_check_instance_not_running(self):
+ """
+ A testcase of driver._live_migration_src_check.
+ The instance given by instance_id is not running.
+ """
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ dest = 'dummydest'
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy',
+ 'volumes':[], 'state_description':'migrating',
+ 'state':power_state.RUNNING}
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_src_check(ctxt, i_ref)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('is not running') > 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_src_check_volume_node_not_alive(self):
+ """
+ A testcase of driver._live_migration_src_check.
+ Volume node is not alive if any volumes are attached to
+ the given instance.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy',
+ 'volumes':[{'id':1}, {'id':2}],
+ 'state_description':'running', 'state':power_state.RUNNING}
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_topic(mox.IgnoreArg(), 'volume').\
+ AndReturn([])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_src_check(ctxt, i_ref)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('volume node is not alive') >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_src_check_volume_node_not_alive(self):
+ """
+ A testcase of driver._live_migration_src_check.
+ The testcase make sure src-compute node is alive.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy', 'volumes':[],
+ 'state_description':'running', 'state':power_state.RUNNING}
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_topic(mox.IgnoreArg(), 'compute').\
+ AndReturn([])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_src_check(ctxt, i_ref)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('is not alive') >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_src_check_works_correctly(self):
+ """
+ A testcase of driver._live_migration_src_check.
+ The testcase make sure everything finished with no error.
+ """
+ driver_i = self.scheduler.driver
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy', 'volumes':[],
+ 'state_description':'running', 'state':power_state.RUNNING}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('host', i_ref['host'])
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_topic(mox.IgnoreArg(), 'compute').\
+ AndReturn([service_ref])
+ self.mox.StubOutWithMock(driver_i, 'service_is_up')
+ driver_i.service_is_up(service_ref).AndReturn(True)
+
+ self.mox.ReplayAll()
+ ret = driver_i._live_migration_src_check(ctxt, i_ref)
+ self.assertTrue(ret == None)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_dest_check_service_not_exists(self):
+ """
+ A testcase of driver._live_migration_dst_check.
+ Destination host does not exist.
+ """
+ driver_i = self.scheduler.driver
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('host', i_ref['host'])
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([])
+
+ self.mox.ReplayAll()
+ try:
+ driver_i._live_migration_dest_check(ctxt, i_ref, dest)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('does not exists') >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_dest_check_service_isnot_compute(self):
+ """
+ A testcase of driver._live_migration_dst_check.
+ Destination host does not provide compute.
+ """
+ driver_i = self.scheduler.driver
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('host', i_ref['host'])
+ service_ref.__setitem__('topic', 'api')
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+
+ self.mox.ReplayAll()
+ try:
+ driver_i._live_migration_dest_check(ctxt, i_ref, dest)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('must be compute node') >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_dest_check_service_not_alive(self):
+ """
+ A testcase of driver._live_migration_dst_check.
+ Destination host compute service is not alive.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('host', i_ref['host'])
+ service_ref.__setitem__('topic', 'compute')
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ self.mox.StubOutWithMock(self.scheduler.driver, 'service_is_up')
+ self.scheduler.driver.service_is_up(service_ref).AndReturn(False)
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_dest_check(ctxt, i_ref, dest)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('is not alive') >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_dest_check_service_same_host(self):
+ """
+ A testcase of driver._live_migration_dst_check.
+ Destination host is same as src host.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummydest'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('host', i_ref['host'])
+ service_ref.__setitem__('topic', 'compute')
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ self.mox.StubOutWithMock(self.scheduler.driver, 'service_is_up')
+ self.scheduler.driver.service_is_up(service_ref).AndReturn(True)
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_dest_check(ctxt, i_ref, dest)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('is running now. choose other host') >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_dest_check_service_works_correctly(self):
+ """
+ A testcase of driver._live_migration_dst_check.
+ The testcase make sure everything finished with no error.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummydest'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('host', i_ref['host'])
+ service_ref.__setitem__('topic', 'compute')
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ self.mox.StubOutWithMock(self.scheduler.driver, 'service_is_up')
+ self.scheduler.driver.service_is_up(service_ref).AndReturn(True)
+ self.mox.StubOutWithMock(self.scheduler.driver, 'has_enough_resource')
+ self.scheduler.driver.has_enough_resource(mox.IgnoreArg(), i_ref, dest)
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_dest_check(ctxt, i_ref, dest)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('is running now. choose other host') >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_common_check_service_dest_not_exists(self):
+ """
+ A testcase of driver._live_migration_common_check.
+ Destination host does not exist.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy'}
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_common_check(ctxt,
+ i_ref,
+ dest)
+ except exception.Invalid, e:
+ self.assertTrue(e.message.find('does not exists') >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_common_check_service_orig_not_exists(self):
+ """
+ A testcase of driver._live_migration_common_check.
+ Original host(an instance launched on) does not exist.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy', 'launched_on':'h1'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('topic', 'compute')
+ service_ref.__setitem__('host', i_ref['host'])
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.service_get_all_by_host(mox.IgnoreArg(),
+ i_ref['launched_on']).\
+ AndReturn([])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_common_check(ctxt,
+ i_ref,
+ dest)
+ except exception.Invalid, e:
+ msg = 'where instance was launched at) does not exists'
+ self.assertTrue(e.message.find(msg) >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_common_check_service_different_hypervisor(self):
+ """
+ A testcase of driver._live_migration_common_check.
+ Original host and dest host has different hypervisor type.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01',
+ 'host':'dummy', 'launched_on':'h1'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('topic', 'compute')
+ service_ref.__setitem__('hypervisor_type', 'kvm')
+ service_ref2 = models.Service()
+ service_ref2.__setitem__('id', 2)
+ service_ref2.__setitem__('hypervisor_type', 'xen')
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.service_get_all_by_host(mox.IgnoreArg(),
+ i_ref['launched_on']).\
+ AndReturn([service_ref2])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_common_check(ctxt,
+ i_ref,
+ dest)
+ except exception.Invalid, e:
+ msg = 'Different hypervisor type'
+ self.assertTrue(e.message.find(msg) >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_common_check_service_different_version(self):
+ """
+ A testcase of driver._live_migration_common_check.
+ Original host and dest host has different hypervisor version.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01',
+ 'host':'dummy', 'launched_on':'h1'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('topic', 'compute')
+ service_ref.__setitem__('hypervisor_version', 12000)
+ service_ref2 = models.Service()
+ service_ref2.__setitem__('id', 2)
+ service_ref2.__setitem__('hypervisor_version', 12001)
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.service_get_all_by_host(mox.IgnoreArg(),
+ i_ref['launched_on']).\
+ AndReturn([service_ref2])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_common_check(ctxt,
+ i_ref,
+ dest)
+ except exception.Invalid, e:
+ msg = 'Older hypervisor version'
+ self.assertTrue(e.message.find(msg) >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_common_check_service_checking_cpuinfo_fail(self):
+ """
+ A testcase of driver._live_migration_common_check.
+ Original host and dest host has different hypervisor version.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01',
+ 'host':'dummy', 'launched_on':'h1'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('topic', 'compute')
+ service_ref.__setitem__('hypervisor_version', 12000)
+ service_ref2 = models.Service()
+ service_ref2.__setitem__('id', 2)
+ service_ref2.__setitem__('hypervisor_version', 12000)
+ service_ref2.__setitem__('cpuinfo', 'info')
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.service_get_all_by_host(mox.IgnoreArg(),
+ i_ref['launched_on']).\
+ AndReturn([service_ref2])
+ driver.db.queue_get_for(mox.IgnoreArg(), FLAGS.compute_topic, dest)
+ self.mox.StubOutWithMock(driver, 'rpc', use_mock_anything=True)
+ driver.rpc.call(mox.IgnoreArg(), mox.IgnoreArg(),
+ {"method": 'compare_cpu',
+ "args": {'cpu_info': service_ref2['cpu_info']}}).\
+ AndRaise(rpc.RemoteError('doesnt have compatibility to', '', ''))
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver._live_migration_common_check(ctxt,
+ i_ref,
+ dest)
+ except rpc.RemoteError, e:
+ msg = 'doesnt have compatibility to'
+ self.assertTrue(e.message.find(msg) >= 0)
+ self.mox.UnsetStubs()
+
+ def test_live_migraiton_common_check_service_works_correctly(self):
+ """
+ A testcase of driver._live_migration_common_check.
+ The testcase make sure everything finished with no error.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ i_ref = {'id':1, 'hostname':'i-01',
+ 'host':'dummy', 'launched_on':'h1'}
+ service_ref = models.Service()
+ service_ref.__setitem__('id', 1)
+ service_ref.__setitem__('topic', 'compute')
+ service_ref.__setitem__('hypervisor_version', 12000)
+ service_ref2 = models.Service()
+ service_ref2.__setitem__('id', 2)
+ service_ref2.__setitem__('hypervisor_version', 12000)
+ service_ref2.__setitem__('cpuinfo', 'info')
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.service_get_all_by_host(mox.IgnoreArg(),
+ i_ref['launched_on']).\
+ AndReturn([service_ref2])
+ driver.db.queue_get_for(mox.IgnoreArg(), FLAGS.compute_topic, dest)
+ self.mox.StubOutWithMock(driver, 'rpc', use_mock_anything=True)
+ driver.rpc.call(mox.IgnoreArg(), mox.IgnoreArg(),
+ {"method": 'compare_cpu',
+ "args": {'cpu_info': service_ref2['cpu_info']}})
+
+ self.mox.ReplayAll()
+ ret = self.scheduler.driver._live_migration_common_check(ctxt,
+ i_ref,
+ dest)
+ self.assertTrue(ret == None)
+ self.mox.UnsetStubs()
+
+ def test_has_enough_resource_lack_resource_vcpu(self):
+ """
+ A testcase of driver.has_enough_resource.
+ Lack of vcpu.(boundary check)
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ service_ref = {'id':1, 'vcpus':16, 'memory_mb':32, 'local_gb':100}
+ i_ref1 = {'id':1, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':6, 'memory_mb':8, 'local_gb':10}
+ i_ref2 = {'id':2, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+ i_ref3 = {'id':3, 'hostname':'i-02', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([i_ref2, i_ref3])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver.has_enough_resource(ctxt, i_ref1, dest)
+ except exception.NotEmpty, e:
+ msg = 'is not capable to migrate'
+ self.assertTrue(e.message.find(msg) >= 0)
+ self.mox.UnsetStubs()
+
+ def test_has_enough_resource_lack_resource_memory(self):
+ """
+ A testcase of driver.has_enough_resource.
+ Lack of memory_mb.(boundary check)
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ service_ref = {'id':1, 'vcpus':16, 'memory_mb':32, 'local_gb':100}
+ i_ref1 = {'id':1, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':16, 'local_gb':10}
+ i_ref2 = {'id':2, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+ i_ref3 = {'id':3, 'hostname':'i-02', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([i_ref2, i_ref3])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver.has_enough_resource(ctxt, i_ref1, dest)
+ except exception.NotEmpty, e:
+ msg = 'is not capable to migrate'
+ self.assertTrue(e.message.find(msg) >= 0)
+ self.mox.UnsetStubs()
+ self.mox.UnsetStubs()
+
+ def test_has_enough_resource_lack_resource_disk(self):
+ """
+ A testcase of driver.has_enough_resource.
+ Lack of local_gb.(boundary check)
+ """
+ scheduler = manager.SchedulerManager()
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ service_ref = {'id':1, 'vcpus':16, 'memory_mb':32, 'local_gb':100}
+ i_ref1 = {'id':1, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':80}
+ i_ref2 = {'id':2, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+ i_ref3 = {'id':3, 'hostname':'i-02', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([i_ref2, i_ref3])
+
+ self.mox.ReplayAll()
+ try:
+ self.scheduler.driver.has_enough_resource(ctxt, i_ref1, dest)
+ except exception.NotEmpty, e:
+ msg = 'is not capable to migrate'
+ self.assertTrue(e.message.find(msg) >= 0)
+ self.mox.UnsetStubs()
+
+ def test_has_enough_resource_works_correctly(self):
+ """
+ A testcase of driver.has_enough_resource
+ to make sure everything finished with no error.
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ service_ref = {'id':1, 'vcpus':16, 'memory_mb':32, 'local_gb':100}
+ i_ref1 = {'id':1, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+ i_ref2 = {'id':2, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+ i_ref3 = {'id':3, 'hostname':'i-02', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+
+ self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
+ driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([service_ref])
+ driver.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
+ AndReturn([i_ref2, i_ref3])
+
+ self.mox.ReplayAll()
+ ret = self.scheduler.driver.has_enough_resource(ctxt, i_ref1, dest)
+ self.assertTrue(ret == None)
+ self.mox.UnsetStubs()
diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py
index a67c8d1e8..a147e69b4 100644
--- a/nova/tests/test_service.py
+++ b/nova/tests/test_service.py
@@ -30,6 +30,7 @@ from nova import rpc
from nova import test
from nova import service
from nova import manager
+from nova.compute import manager as compute_manager
FLAGS = flags.FLAGS
flags.DEFINE_string("fake_manager", "nova.tests.test_service.FakeManager",
@@ -41,7 +42,20 @@ class FakeManager(manager.Manager):
def test_method(self):
return 'manager'
-
+# temporary variable to store host/binary/self.mox from each method to fake class.
+global_host = None
+global_binary = None
+global_mox = None
+class FakeComputeManager(compute_manager.ComputeManager):
+ """Fake computemanager manager for tests"""
+
+ def __init__(self, compute_driver=None, *args, **kwargs):
+ global ghost, gbinary, gmox
+ self.update_service(mox.IgnoreArg(), mox.StrContains(ghost), mox.StrContains(gbinary))
+ gmox.ReplayAll()
+ super(FakeComputeManager, self).__init__(compute_driver, *args, **kwargs)
+
+
class ExtendedService(service.Service):
def test_method(self):
return 'service'
@@ -258,3 +272,48 @@ class ServiceTestCase(test.TestCase):
serv.report_state()
self.assert_(not serv.model_disconnected)
+
+ def test_compute_can_update_services(self):
+ """
+ Test nova-compute successfully updated Service table on DB.
+ Doing so, self.manager.update_service must be called
+ if 'self.binary == nova-compute', and this testcase checks on it.
+ """
+ host = 'foo'
+ binary = 'nova-compute'
+ topic = 'compute1'
+ service_create = {'host': host,
+ 'binary': binary,
+ 'topic': topic,
+ 'report_count': 0,
+ 'availability_zone': 'nova'}
+ service_ref = {'host': host,
+ 'binary': binary,
+ 'topic': topic,
+ 'report_count': 0,
+ 'availability_zone': 'nova',
+ 'id': 1}
+
+ service.db.service_get_by_args(mox.IgnoreArg(),
+ host,
+ binary).AndRaise(exception.NotFound())
+ service.db.service_create(mox.IgnoreArg(),
+ service_create).AndReturn(service_ref)
+ self.mox.StubOutWithMock(compute_manager.ComputeManager, 'update_service')
+
+
+ global ghost, gbinary, gmox
+ ghost = host
+ gbinary = binary
+ gmox = self.mox
+
+ serv = service.Service(host,
+ binary,
+ topic,
+ 'nova.tests.test_service.FakeComputeManager')
+ # ReplayAll has been executed FakeComputeManager.__init__()
+ #self.mox.ReplayAll()
+ serv.start()
+ serv.stop()
+
+
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index afdc89ba2..177e8f021 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -14,21 +14,29 @@
# License for the specific language governing permissions and limitations
# under the License.
+import mox
+
from xml.etree.ElementTree import fromstring as xml_to_tree
from xml.dom.minidom import parseString as xml_to_dom
from nova import context
from nova import db
+from nova import exception
from nova import flags
from nova import test
+from nova import logging
from nova import utils
from nova.api.ec2 import cloud
from nova.auth import manager
+from nova.db.sqlalchemy import models
+from nova.compute import power_state
from nova.virt import libvirt_conn
FLAGS = flags.FLAGS
flags.DECLARE('instances_path', 'nova.compute.manager')
+libvirt = None
+libxml2 = None
class LibvirtConnTestCase(test.TestCase):
def setUp(self):
@@ -52,6 +60,38 @@ class LibvirtConnTestCase(test.TestCase):
'bridge': 'br101',
'instance_type': 'm1.small'}
+ def _driver_dependent_test_setup(self):
+ """
+ Setup method.
+ Call this method at the top of each testcase method,
+ if the testcase is necessary libvirt and cheetah.
+ """
+ try :
+ global libvirt
+ global libxml2
+ libvirt_conn.libvirt = __import__('libvirt')
+ libvirt_conn.libxml2 = __import__('libxml2')
+ libvirt_conn._late_load_cheetah()
+ libvirt = __import__('libvirt')
+ except ImportError, e:
+ logging.warn("""This test has not been done since """
+ """using driver-dependent library Cheetah/libvirt/libxml2.""")
+ raise e
+
+ # inebitable mocks for calling
+ #nova.virt.libvirt_conn.LibvirtConnection.__init__
+ nwmock = self.mox.CreateMock(libvirt_conn.NWFilterFirewall)
+ self.mox.StubOutWithMock(libvirt_conn, 'NWFilterFirewall',
+ use_mock_anything=True)
+ libvirt_conn.NWFilterFirewall(mox.IgnoreArg()).AndReturn(nwmock)
+
+ obj = utils.import_object(FLAGS.firewall_driver)
+ fwmock = self.mox.CreateMock(obj)
+ self.mox.StubOutWithMock(libvirt_conn, 'utils',
+ use_mock_anything=True)
+ libvirt_conn.utils.import_object(FLAGS.firewall_driver).AndReturn(fwmock)
+ return nwmock, fwmock
+
def test_xml_and_uri_no_ramdisk_no_kernel(self):
instance_data = dict(self.test_instance)
self._check_xml_and_uri(instance_data,
@@ -188,9 +228,8 @@ class LibvirtConnTestCase(test.TestCase):
expected_result,
'%s failed common check %d' % (xml, i))
- # This test is supposed to make sure we don't override a specifically
- # set uri
- #
+ # This test is supposed to make sure we don't override a specifically set uri
+ #
# Deliberately not just assigning this string to FLAGS.libvirt_uri and
# checking against that later on. This way we make sure the
# implementation doesn't fiddle around with the FLAGS.
@@ -202,6 +241,480 @@ class LibvirtConnTestCase(test.TestCase):
uri = conn.get_uri()
self.assertEquals(uri, testuri)
+ def test_get_memory_mb(self):
+ """
+ Check if get_memory_mb returns memory value
+ Connection/OS/driver differenct does not matter for this method,
+ so everyone can execute for checking.
+ """
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertTrue(0 < conn.get_memory_mb())
+ self.mox.UnsetStubs()
+
+ def test_get_cpu_info_works_correctly(self):
+ """
+ Check if get_cpu_info works correctly.
+ (in case libvirt.getCapabilities() works correctly)
+ """
+ xml=("""x86_64Nehalem"""
+ """Intel"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """""")
+
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn', use_mock_anything=True)
+ libvirt_conn.LibvirtConnection._conn.getCapabilities().AndReturn(xml)
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertTrue(0 < len(conn.get_cpu_info()))
+ self.mox.UnsetStubs()
+
+ def test_get_cpu_info_inappropreate_xml(self):
+ """
+ Check if get_cpu_info raises exception
+ in case libvirt.getCapabilities() returns wrong xml
+ (in case of xml doesnt have tag)
+ """
+ xml=("""x86_64Nehalem"""
+ """Intel"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """""")
+
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn', use_mock_anything=True)
+ libvirt_conn.LibvirtConnection._conn.getCapabilities().AndReturn(xml)
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ try:
+ conn.get_cpu_info()
+ except exception.Invalid, e:
+ c1 = ( 0 <= e.message.find('Invalid xml') )
+ self.assertTrue(c1)
+ self.mox.UnsetStubs()
+
+ def test_get_cpu_info_inappropreate_xml2(self):
+ """
+ Check if get_cpu_info raises exception
+ in case libvirt.getCapabilities() returns wrong xml
+ (in case of xml doesnt have inproper tag
+ meaning missing "socket" attribute)
+ """
+ xml=("""x86_64Nehalem"""
+ """Intel"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """"""
+ """""")
+
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn', use_mock_anything=True)
+ libvirt_conn.LibvirtConnection._conn.getCapabilities().AndReturn(xml)
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ try:
+ conn.get_cpu_info()
+ except exception.Invalid, e:
+ c1 = ( 0 <= e.message.find('Invalid xml: topology') )
+ self.assertTrue(c1)
+ self.mox.UnsetStubs()
+
+ def test_compare_cpu_works_correctly(self):
+ """Calling libvirt.compute_cpu() and works correctly """
+
+ t = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
+ """"topology":{"cores":"%s", "threads":"%s", """
+ """"sockets":"%s"}, "features":[%s]}""")
+ cpu_info = t % ('x86', 'model', 'vendor', '2', '1', '4', '"tm"')
+
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn', use_mock_anything=True)
+ libvirt_conn.LibvirtConnection._conn.compareCPU(mox.IgnoreArg(),0).AndReturn(1)
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertTrue( None== conn.compare_cpu(cpu_info))
+ self.mox.UnsetStubs()
+
+ def test_compare_cpu_raises_exception(self):
+ """
+ Libvirt-related exception occurs when calling
+ libvirt.compare_cpu().
+ """
+ t = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
+ """"topology":{"cores":"%s", "threads":"%s", """
+ """"sockets":"%s"}, "features":[%s]}""")
+ cpu_info = t % ('x86', 'model', 'vendor', '2', '1', '4', '"tm"')
+
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn',
+ use_mock_anything=True)
+ libvirt_conn.LibvirtConnection._conn.compareCPU(mox.IgnoreArg(),0).\
+ AndRaise(libvirt.libvirtError('ERR'))
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertRaises(libvirt.libvirtError, conn.compare_cpu, cpu_info)
+ self.mox.UnsetStubs()
+
+ def test_compare_cpu_no_compatibility(self):
+ """libvirt.compare_cpu() return less than 0.(no compatibility)"""
+
+ t = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
+ """"topology":{"cores":"%s", "threads":"%s", """
+ """"sockets":"%s"}, "features":[%s]}""")
+ cpu_info = t % ('x86', 'model', 'vendor', '2', '1', '4', '"tm"')
+
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn',
+ use_mock_anything=True)
+ libvirt_conn.LibvirtConnection._conn.compareCPU(mox.IgnoreArg(),0).\
+ AndRaise(exception.Invalid('ERR'))
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertRaises(exception.Invalid, conn.compare_cpu, cpu_info)
+ self.mox.UnsetStubs()
+
+ def test_ensure_filtering_rules_for_instance_works_correctly(self):
+ """ensure_filtering_rules_for_instance works as expected correctly"""
+
+ instance_ref = models.Instance()
+ instance_ref.__setitem__('id', 1)
+
+ try:
+ nwmock, fwmock = self._driver_dependent_test_setup()
+ except:
+ return
+
+ nwmock.setup_basic_filtering(mox.IgnoreArg())
+ fwmock.prepare_instance_filter(instance_ref)
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn',
+ use_mock_anything=True)
+ n = 'nova-instance-%s' % instance_ref.name
+ libvirt_conn.LibvirtConnection._conn.nwfilterLookupByName(n)
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ conn.ensure_filtering_rules_for_instance(instance_ref)
+ self.mox.UnsetStubs()
+
+ def test_ensure_filtering_rules_for_instance_timeout(self):
+ """ensure_filtering_fules_for_instance finishes with timeout"""
+
+ instance_ref = models.Instance()
+ instance_ref.__setitem__('id', 1)
+
+ try:
+ nwmock, fwmock = self._driver_dependent_test_setup()
+ except:
+ return
+
+ nwmock.setup_basic_filtering(mox.IgnoreArg())
+ fwmock.prepare_instance_filter(instance_ref)
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn',
+ use_mock_anything=True)
+ n = 'nova-instance-%s' % instance_ref.name
+ for i in range(FLAGS.live_migration_timeout_sec * 2):
+ libvirt_conn.LibvirtConnection._conn.\
+ nwfilterLookupByName(n).AndRaise(libvirt.libvirtError('ERR'))
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ try:
+ conn.ensure_filtering_rules_for_instance(instance_ref)
+ except exception.Error, e:
+ c1 = ( 0<=e.message.find('Timeout migrating for'))
+ self.assertTrue(c1)
+ self.mox.UnsetStubs()
+
+ def test_live_migration_works_correctly(self):
+ """_live_migration works as expected correctly """
+
+ class dummyCall(object):
+ f = None
+ def start(self, interval=0, now=False):
+ pass
+
+ instance_ref = models.Instance()
+ instance_ref.__setitem__('id', 1)
+ dest = 'desthost'
+ ctxt = context.get_admin_context()
+
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn',
+ use_mock_anything=True)
+ vdmock = self.mox.CreateMock(libvirt.virDomain)
+ self.mox.StubOutWithMock(vdmock, "migrateToURI",
+ use_mock_anything=True)
+ vdmock.migrateToURI(FLAGS.live_migration_uri % dest, mox.IgnoreArg(),
+ None, FLAGS.live_migration_bandwidth).\
+ AndReturn(None)
+ libvirt_conn.LibvirtConnection._conn.lookupByName(instance_ref.name).\
+ AndReturn(vdmock)
+ # below description is also ok.
+ #self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection._conn,
+ # "lookupByName", use_mock_anything=True)
+
+ libvirt_conn.utils.LoopingCall(f=None).AndReturn(dummyCall())
+
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ ret = conn._live_migration(ctxt, instance_ref, dest)
+ self.assertTrue(ret == None)
+ self.mox.UnsetStubs()
+
+ def test_live_migration_raises_exception(self):
+ """
+ _live_migration raises exception, then this testcase confirms
+ state_description/state for the instances/volumes are recovered.
+ """
+ class Instance(models.NovaBase):
+ id = 0
+ volumes = None
+ name = 'name'
+
+ ctxt = context.get_admin_context()
+ dest = 'desthost'
+ instance_ref = Instance()
+ instance_ref.__setitem__('id', 1)
+ instance_ref.__setitem__('volumes', [{'id':1}, {'id':2}])
+
+ try:
+ nwmock, fwmock = self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn',
+ use_mock_anything=True)
+ vdmock = self.mox.CreateMock(libvirt.virDomain)
+ self.mox.StubOutWithMock(vdmock, "migrateToURI",
+ use_mock_anything=True)
+ vdmock.migrateToURI(FLAGS.live_migration_uri % dest, mox.IgnoreArg(),
+ None, FLAGS.live_migration_bandwidth).\
+ AndRaise(libvirt.libvirtError('ERR'))
+ libvirt_conn.LibvirtConnection._conn.lookupByName(instance_ref.name).\
+ AndReturn(vdmock)
+ self.mox.StubOutWithMock(db, 'instance_set_state')
+ db.instance_set_state(ctxt, instance_ref['id'],
+ power_state.RUNNING, 'running')
+ self.mox.StubOutWithMock(db, 'volume_update')
+ for v in instance_ref.volumes:
+ db.volume_update(ctxt, v['id'], {'status': 'in-use'}).\
+ InAnyOrder('g1')
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertRaises(libvirt.libvirtError,
+ conn._live_migration,
+ ctxt, instance_ref, dest)
+ self.mox.UnsetStubs()
+
+ def test_post_live_migration_working_correctly(self):
+ """_post_live_migration works as expected correctly """
+
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ instance_ref = {'id':1, 'hostname':'i-00000001', 'host':dest,
+ 'fixed_ip':'dummyip', 'floating_ip':'dummyflip',
+ 'volumes':[{'id':1}, {'id':2} ]}
+ network_ref = {'id':1, 'host':dest}
+ floating_ip_ref = {'id':1, 'address':'1.1.1.1'}
+
+ try:
+ nwmock, fwmock = self._driver_dependent_test_setup()
+ except:
+ return
+ fwmock.unfilter_instance(instance_ref)
+
+ fixed_ip = instance_ref['fixed_ip']
+ self.mox.StubOutWithMock(db, 'instance_get_fixed_address')
+ db.instance_get_fixed_address(ctxt, instance_ref['id']).AndReturn(fixed_ip)
+ self.mox.StubOutWithMock(db, 'fixed_ip_update')
+ db.fixed_ip_update(ctxt, fixed_ip, {'host': dest})
+ self.mox.StubOutWithMock(db, 'fixed_ip_get_network')
+ db.fixed_ip_get_network(ctxt, fixed_ip).AndReturn(network_ref)
+ self.mox.StubOutWithMock(db, 'network_update')
+ db.network_update(ctxt, network_ref['id'], {'host': dest})
+
+ fl_ip = instance_ref['floating_ip']
+ self.mox.StubOutWithMock(db, 'instance_get_floating_address')
+ db.instance_get_floating_address(ctxt, instance_ref['id']).AndReturn(fl_ip)
+ self.mox.StubOutWithMock(db, 'floating_ip_get_by_address')
+ db.floating_ip_get_by_address(ctxt, instance_ref['floating_ip']).\
+ AndReturn(floating_ip_ref)
+ self.mox.StubOutWithMock(db, 'floating_ip_update')
+ db.floating_ip_update(ctxt, floating_ip_ref['address'], {'host': dest})
+
+ self.mox.StubOutWithMock(db, 'instance_update')
+ db.instance_update(ctxt, instance_ref['id'],
+ {'state_description': 'running',
+ 'state': power_state.RUNNING, 'host': dest})
+ self.mox.StubOutWithMock(db, 'volume_update')
+ for v in instance_ref['volumes']:
+ db.volume_update(ctxt, v['id'], {'status': 'in-use'})
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ conn._post_live_migration( ctxt, instance_ref, dest)
+ self.mox.UnsetStubs()
+
+ def test_post_live_migration_no_floating_ip(self):
+ """
+ _post_live_migration works as expected correctly
+ (in case instance doesnt have floaitng ip)
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ instance_ref = {'id':1, 'hostname':'i-00000001', 'host':dest,
+ 'fixed_ip':'dummyip', 'floating_ip':'dummyflip',
+ 'volumes':[{'id':1}, {'id':2} ]}
+ network_ref = {'id':1, 'host':dest}
+ floating_ip_ref = {'id':1, 'address':'1.1.1.1'}
+
+ try:
+ nwmock, fwmock = self._driver_dependent_test_setup()
+ except:
+ return
+ fwmock.unfilter_instance(instance_ref)
+
+ fixed_ip = instance_ref['fixed_ip']
+ self.mox.StubOutWithMock(db, 'instance_get_fixed_address')
+ db.instance_get_fixed_address(ctxt, instance_ref['id']).AndReturn(fixed_ip)
+ self.mox.StubOutWithMock(db, 'fixed_ip_update')
+ db.fixed_ip_update(ctxt, fixed_ip, {'host': dest})
+ self.mox.StubOutWithMock(db, 'fixed_ip_get_network')
+ db.fixed_ip_get_network(ctxt, fixed_ip).AndReturn(network_ref)
+ self.mox.StubOutWithMock(db, 'network_update')
+ db.network_update(ctxt, network_ref['id'], {'host': dest})
+
+ self.mox.StubOutWithMock(db, 'instance_get_floating_address')
+ db.instance_get_floating_address(ctxt, instance_ref['id']).AndReturn(None)
+ self.mox.StubOutWithMock(libvirt_conn.LOG, 'info')
+ libvirt_conn.LOG.info(_('post livemigration operation is started..'))
+ libvirt_conn.LOG.info(_('floating_ip is not found for %s'),
+ instance_ref['hostname'])
+ # Checking last messages are ignored. may be no need to check so strictly?
+ libvirt_conn.LOG.info(mox.IgnoreArg())
+ libvirt_conn.LOG.info(mox.IgnoreArg())
+
+ self.mox.StubOutWithMock(db, 'instance_update')
+ db.instance_update(ctxt, instance_ref['id'],
+ {'state_description': 'running',
+ 'state': power_state.RUNNING,
+ 'host': dest})
+ self.mox.StubOutWithMock(db, 'volume_update')
+ for v in instance_ref['volumes']:
+ db.volume_update(ctxt, v['id'], {'status': 'in-use'})
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ conn._post_live_migration( ctxt, instance_ref, dest)
+ self.mox.UnsetStubs()
+
+ def test_post_live_migration_no_floating_ip_with_exception(self):
+ """
+ _post_live_migration works as expected correctly
+ (in case instance doesnt have floaitng ip, and raise exception)
+ """
+ dest = 'dummydest'
+ ctxt = context.get_admin_context()
+ instance_ref = {'id':1, 'hostname':'i-00000001', 'host':dest,
+ 'fixed_ip':'dummyip', 'floating_ip':'dummyflip',
+ 'volumes':[{'id':1}, {'id':2} ]}
+ network_ref = {'id':1, 'host':dest}
+ floating_ip_ref = {'id':1, 'address':'1.1.1.1'}
+
+ try:
+ nwmock, fwmock = self._driver_dependent_test_setup()
+ except:
+ return
+ fwmock.unfilter_instance(instance_ref)
+
+ fixed_ip = instance_ref['fixed_ip']
+ self.mox.StubOutWithMock(db, 'instance_get_fixed_address')
+ db.instance_get_fixed_address(ctxt, instance_ref['id']).AndReturn(fixed_ip)
+ self.mox.StubOutWithMock(db, 'fixed_ip_update')
+ db.fixed_ip_update(ctxt, fixed_ip, {'host': dest})
+ self.mox.StubOutWithMock(db, 'fixed_ip_get_network')
+ db.fixed_ip_get_network(ctxt, fixed_ip).AndReturn(network_ref)
+ self.mox.StubOutWithMock(db, 'network_update')
+ db.network_update(ctxt, network_ref['id'], {'host': dest})
+
+ self.mox.StubOutWithMock(db, 'instance_get_floating_address')
+ db.instance_get_floating_address(ctxt, instance_ref['id']).\
+ AndRaise(exception.NotFound())
+ self.mox.StubOutWithMock(libvirt_conn.LOG, 'info')
+ libvirt_conn.LOG.info(_('post livemigration operation is started..'))
+ libvirt_conn.LOG.info(_('floating_ip is not found for %s'),
+ instance_ref['hostname'])
+ # the last message is ignored. may be no need to check so strictly?
+ libvirt_conn.LOG.info(mox.IgnoreArg())
+ libvirt_conn.LOG.info(mox.IgnoreArg())
+
+ self.mox.StubOutWithMock(db, 'instance_update')
+ db.instance_update(ctxt, instance_ref['id'],
+ {'state_description': 'running',
+ 'state': power_state.RUNNING, 'host': dest})
+ self.mox.StubOutWithMock(db, 'volume_update')
+ for v in instance_ref['volumes']:
+ db.volume_update(ctxt, v['id'], {'status': 'in-use'})
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ conn._post_live_migration( ctxt, instance_ref, dest)
+ self.mox.UnsetStubs()
+
def tearDown(self):
super(LibvirtConnTestCase, self).tearDown()
self.manager.delete_project(self.project)
@@ -475,3 +988,4 @@ class NWFilterTestCase(test.TestCase):
self.fw.prepare_instance_filter(instance)
_ensure_all_called()
self.teardown_security_group()
+
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index 80ae7f34c..f469af681 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -316,15 +316,15 @@ class FakeConnection(object):
def get_vcpu_number(self):
"""This method is supported only libvirt. """
- return -1
+ return
def get_memory_mb(self):
"""This method is supported only libvirt.."""
- return -1
+ return
def get_local_gb(self):
"""This method is supported only libvirt.."""
- return -1
+ return
def get_hypervisor_type(self):
"""This method is supported only libvirt.."""
@@ -332,12 +332,16 @@ class FakeConnection(object):
def get_hypervisor_version(self):
"""This method is supported only libvirt.."""
- return -1
+ return
def compare_cpu(self, xml):
"""This method is supported only libvirt.."""
raise NotImplementedError('This method is supported only libvirt.')
+ def ensure_filtering_rules_for_instance(self, instance_ref):
+ """This method is supported only libvirt.."""
+ raise NotImplementedError('This method is supported only libvirt.')
+
def live_migration(self, context, instance_ref, dest):
"""This method is supported only libvirt.."""
raise NotImplementedError('This method is supported only libvirt.')
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 7d1f76b32..49dd03c57 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -861,18 +861,18 @@ class LibvirtConnection(object):
def get_cpu_info(self):
""" Get cpuinfo information """
- xmlstr = self._conn.getCapabilities()
- xml = libxml2.parseDoc(xmlstr)
+ xml = self._conn.getCapabilities()
+ xml = libxml2.parseDoc(xml)
nodes = xml.xpathEval('//cpu')
if len(nodes) != 1:
- msg = 'Unexpected xml format. tag "cpu" must be 1, but %d.' \
- % len(nodes)
+ msg = 'Invalid xml. "" must be 1, but %d.' % len(nodes)
msg += '\n' + xml.serialize()
raise exception.Invalid(_(msg))
- arch = xml.xpathEval('//cpu/arch')[0].getContent()
- model = xml.xpathEval('//cpu/model')[0].getContent()
- vendor = xml.xpathEval('//cpu/vendor')[0].getContent()
+ cpu_info = dict()
+ cpu_info['arch'] = xml.xpathEval('//cpu/arch')[0].getContent()
+ cpu_info['model'] = xml.xpathEval('//cpu/model')[0].getContent()
+ cpu_info['vendor'] = xml.xpathEval('//cpu/vendor')[0].getContent()
topology_node = xml.xpathEval('//cpu/topology')[0].get_properties()
topology = dict()
@@ -890,18 +890,19 @@ class LibvirtConnection(object):
feature_nodes = xml.xpathEval('//cpu/feature')
features = list()
for nodes in feature_nodes:
- feature_name = nodes.get_properties().getContent()
- features.append(feature_name)
+ features.append(nodes.get_properties().getContent())
template = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
""""topology":{"cores":"%s", "threads":"%s", """
""""sockets":"%s"}, "features":[%s]}""")
- c = topology['cores']
- s = topology['sockets']
- t = topology['threads']
f = ['"%s"' % x for x in features]
- cpu_info = template % (arch, model, vendor, c, s, t, ', '.join(f))
- return cpu_info
+ return template % (cpu_info['arch'],
+ cpu_info['model'],
+ cpu_info['vendor'],
+ topology['cores'],
+ topology['sockets'],
+ topology['threads'],
+ ', '.join(f))
def block_stats(self, instance_name, disk):
"""
@@ -935,12 +936,12 @@ class LibvirtConnection(object):
def compare_cpu(self, cpu_info):
"""
- Check the host cpu is compatible to a cpu given by xml.
- "xml" must be a part of libvirt.openReadonly().getCapabilities().
- return values follows by virCPUCompareResult.
- if 0 > return value, do live migration.
+ Check the host cpu is compatible to a cpu given by xml.
+ "xml" must be a part of libvirt.openReadonly().getCapabilities().
+ return values follows by virCPUCompareResult.
+ if 0 > return value, do live migration.
- 'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult'
+ 'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult'
"""
msg = _('Checking cpu_info: instance was launched this cpu.\n: %s ')
LOG.info(msg % cpu_info)
@@ -952,7 +953,7 @@ class LibvirtConnection(object):
url = 'http://libvirt.org/html/libvirt-libvirt.html'
url += '#virCPUCompareResult\n'
msg = 'CPU does not have compativility.\n'
- msg += 'result:%d \n'
+ msg += 'result:%s \n'
msg += 'Refer to %s'
msg = _(msg)
@@ -960,7 +961,7 @@ class LibvirtConnection(object):
try:
ret = self._conn.compareCPU(xml, 0)
except libvirt.libvirtError, e:
- LOG.error(msg % (ret, url))
+ LOG.error(msg % (e.message, url))
raise e
if ret <= 0:
@@ -969,24 +970,26 @@ class LibvirtConnection(object):
return
def ensure_filtering_rules_for_instance(self, instance_ref):
- """ Setting up inevitable filtering rules on compute node,
- and waiting for its completion.
- To migrate an instance, filtering rules to hypervisors
- and firewalls are inevitable on destination host.
- ( Waiting only for filterling rules to hypervisor,
- since filtering rules to firewall rules can be set faster).
-
- Concretely, the below method must be called.
- - setup_basic_filtering (for nova-basic, etc.)
- - prepare_instance_filter(for nova-instance-instance-xxx, etc.)
-
- to_xml may have to be called since it defines PROJNET, PROJMASK.
- but libvirt migrates those value through migrateToURI(),
- so , no need to be called.
-
- Don't use thread for this method since migration should
- not be started when setting-up filtering rules operations
- are not completed."""
+ """
+ Setting up inevitable filtering rules on compute node,
+ and waiting for its completion.
+ To migrate an instance, filtering rules to hypervisors
+ and firewalls are inevitable on destination host.
+ ( Waiting only for filterling rules to hypervisor,
+ since filtering rules to firewall rules can be set faster).
+
+ Concretely, the below method must be called.
+ - setup_basic_filtering (for nova-basic, etc.)
+ - prepare_instance_filter(for nova-instance-instance-xxx, etc.)
+
+ to_xml may have to be called since it defines PROJNET, PROJMASK.
+ but libvirt migrates those value through migrateToURI(),
+ so , no need to be called.
+
+ Don't use thread for this method since migration should
+ not be started when setting-up filtering rules operations
+ are not completed.
+ """
# Tf any instances never launch at destination host,
# basic-filtering must be set here.
@@ -1009,40 +1012,44 @@ class LibvirtConnection(object):
raise exception.Error(msg % (ec2_id, instance_ref.name))
time.sleep(0.5)
- def live_migration(self, context, instance_ref, dest):
+ def live_migration(self, ctxt, instance_ref, dest):
"""
- Just spawning live_migration operation for
- distributing high-load.
+ Just spawning live_migration operation for
+ distributing high-load.
"""
- greenthread.spawn(self._live_migration, context, instance_ref, dest)
+ greenthread.spawn(self._live_migration, ctxt, instance_ref, dest)
- def _live_migration(self, context, instance_ref, dest):
+ def _live_migration(self, ctxt, instance_ref, dest):
""" Do live migration."""
# Do live migration.
try:
- duri = FLAGS.live_migration_uri % dest
-
flaglist = FLAGS.live_migration_flag.split(',')
flagvals = [getattr(libvirt, x.strip()) for x in flaglist]
logical_sum = reduce(lambda x, y: x | y, flagvals)
- bandwidth = FLAGS.live_migration_bandwidth
-
if self.read_only:
tmpconn = self._connect(self.libvirt_uri, False)
dom = tmpconn.lookupByName(instance_ref.name)
- dom.migrateToURI(duri, logical_sum, None, bandwidth)
+ dom.migrateToURI(FLAGS.live_migration_uri % dest,
+ logical_sum,
+ None,
+ FLAGS.live_migration_bandwidth)
tmpconn.close()
else:
dom = self._conn.lookupByName(instance_ref.name)
- dom.migrateToURI(duri, logical_sum, None, bandwidth)
+ dom.migrateToURI(FLAGS.live_migration_uri % dest,
+ logical_sum,
+ None,
+ FLAGS.live_migration_bandwidth)
except Exception, e:
- id = instance_ref['id']
- db.instance_set_state(context, id, power_state.RUNNING, 'running')
+ db.instance_set_state(ctxt,
+ instance_ref['id'],
+ power_state.RUNNING,
+ 'running')
for v in instance_ref['volumes']:
- db.volume_update(context,
+ db.volume_update(ctxt,
v['id'],
{'status': 'in-use'})
@@ -1052,20 +1059,20 @@ class LibvirtConnection(object):
timer = utils.LoopingCall(f=None)
def wait_for_live_migration():
-
+ """waiting for live migration completion"""
try:
- state = self.get_info(instance_ref.name)['state']
+ self.get_info(instance_ref.name)['state']
except exception.NotFound:
timer.stop()
- self._post_live_migration(context, instance_ref, dest)
+ self._post_live_migration(ctxt, instance_ref, dest)
timer.f = wait_for_live_migration
timer.start(interval=0.5, now=True)
- def _post_live_migration(self, context, instance_ref, dest):
+ def _post_live_migration(self, ctxt, instance_ref, dest):
"""
- Post operations for live migration.
- Mainly, database updating.
+ Post operations for live migration.
+ Mainly, database updating.
"""
LOG.info('post livemigration operation is started..')
# Detaching volumes.
@@ -1079,61 +1086,61 @@ class LibvirtConnection(object):
'nova.virt.libvirt_conn.IptablesFirewallDriver':
try:
self.firewall_driver.unfilter_instance(instance_ref)
- except KeyError, e:
+ except KeyError:
pass
# Database updating.
ec2_id = instance_ref['hostname']
instance_id = instance_ref['id']
- fixed_ip = db.instance_get_fixed_address(context, instance_id)
+ fixed_ip = db.instance_get_fixed_address(ctxt, instance_id)
# Not return if fixed_ip is not found, otherwise,
# instance never be accessible..
if None == fixed_ip:
logging.warn('fixed_ip is not found for %s ' % ec2_id)
- db.fixed_ip_update(context, fixed_ip, {'host': dest})
- network_ref = db.fixed_ip_get_network(context, fixed_ip)
- db.network_update(context, network_ref['id'], {'host': dest})
+ db.fixed_ip_update(ctxt, fixed_ip, {'host': dest})
+ network_ref = db.fixed_ip_get_network(ctxt, fixed_ip)
+ db.network_update(ctxt, network_ref['id'], {'host': dest})
try:
floating_ip \
- = db.instance_get_floating_address(context, instance_id)
+ = db.instance_get_floating_address(ctxt, instance_id)
# Not return if floating_ip is not found, otherwise,
# instance never be accessible..
if None == floating_ip:
- logging.error('floating_ip is not found for %s ' % ec2_id)
+ LOG.info(_('floating_ip is not found for %s'), ec2_id)
else:
- floating_ip_ref = db.floating_ip_get_by_address(context,
+ floating_ip_ref = db.floating_ip_get_by_address(ctxt,
floating_ip)
- db.floating_ip_update(context,
+ db.floating_ip_update(ctxt,
floating_ip_ref['address'],
{'host': dest})
except exception.NotFound:
- logging.debug('%s doesnt have floating_ip.. ' % ec2_id)
+ LOG.info(_('floating_ip is not found for %s'), ec2_id)
except:
- msg = 'Live migration: Unexpected error:'
- msg += '%s cannot inherit floating ip.. ' % ec2_id
- logging.error(_(msg))
+ msg = ("""Live migration: Unexpected error:"""
+ """%s cannot inherit floating ip..""")
+ LOG.error(_(msg), ec2_id)
# Restore instance/volume state
- db.instance_update(context,
+ db.instance_update(ctxt,
instance_id,
{'state_description': 'running',
'state': power_state.RUNNING,
'host': dest})
for v in instance_ref['volumes']:
- db.volume_update(context,
+ db.volume_update(ctxt,
v['id'],
{'status': 'in-use'})
- logging.info(_('Live migrating %s to %s finishes successfully')
+ LOG.info(_('Live migrating %s to %s finishes successfully')
% (ec2_id, dest))
msg = _(("""Known error: the below error is nomally occurs.\n"""
"""Just check if iinstance is successfully migrated.\n"""
"""libvir: QEMU error : Domain not found: no domain """
"""with matching name.."""))
- logging.info(msg)
+ LOG.info(msg)
class FirewallDriver(object):
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index c10f73fe7..1e7933f51 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -215,15 +215,15 @@ class XenAPIConnection(object):
def get_vcpu_number(self):
"""This method is supported only libvirt. """
- return -1
+ return
def get_memory_mb(self):
"""This method is supported only libvirt.."""
- return -1
+ return
def get_local_gb(self):
"""This method is supported only libvirt.."""
- return -1
+ return
def get_hypervisor_type(self):
"""This method is supported only libvirt.."""
@@ -231,12 +231,18 @@ class XenAPIConnection(object):
def get_hypervisor_version(self):
"""This method is supported only libvirt.."""
- return -1
+ return
def compare_cpu(self, xml):
+ """This method is supported only libvirt.."""
+ raise NotImplementedError('This method is supported only libvirt.')
+
+ def ensure_filtering_rules_for_instance(self, instance_ref):
+ """This method is supported only libvirt.."""
raise NotImplementedError('This method is supported only libvirt.')
def live_migration(self, context, instance_ref, dest):
+ """This method is supported only libvirt.."""
raise NotImplementedError('This method is supported only libvirt.')
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index 1735d79eb..906eb86ea 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -153,6 +153,6 @@ class VolumeManager(manager.Manager):
def check_for_export(self, context, instance_id):
"""Make sure whether volume is exported."""
if FLAGS.volume_driver == 'nova.volume.driver.AOEDriver':
- instance_ref = self.db.instance_get(instance_id)
+ instance_ref = self.db.instance_get(context, instance_id)
for v in instance_ref['volumes']:
self.driver.check_for_export(context, v['id'])
--
cgit
From 09f2c4729456443c4874a8cadc53299817d6371a Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Mon, 31 Jan 2011 18:41:10 +0900
Subject: 1. Discard nova-manage host list Reason: nova-manage service list can
be replacement. Changes: nova-manage
2. Fix resource checking inappropriate design.
Reason:
nova.scheduler.driver.has_enough_resource has inappropriate design,
so fix it. This method didnt check free memory but check total memory.
We need to register free memory onto databases(periodically).
But periodically updating may causes flooding request to db in case of
many compute-node. Currently, since memory information is only used in
this feature, we take the choice that administrators manually has to
execute nova-manage to let compute node update their own memory information.
Changes:
nova.db.sqlalchemy.models
- Adding memory_mb_used, local_gb_used, vcpu_used column to Service.
(local_gb and vcpu is just for reference to admins for now)
nova.compute.manager
- Changing nova.compute.manager.update_service
Service table column is changed, so updating method must be changed.
- Adding nova.compute.manager.update_available_resource
a responder to admin's request to let compute nodes update their
memory infomation
nova.virt.libvirt_conn
nova.virt.xenapi_conn
nova.virt.fake
- Adding getter method for memory_mb_used/local_gb_used/vcpu_used.
nova-manage
- request method to let compute nodes update their own memory info.
---
bin/nova-manage | 92 ++++++++++++-----------
nova/compute/manager.py | 54 ++++++++++++-
nova/db/sqlalchemy/models.py | 5 +-
nova/rpc.py | 3 +
nova/scheduler/driver.py | 67 +++++++++++------
nova/scheduler/manager.py | 11 ++-
nova/tests/test_compute.py | 55 ++++++++++++--
nova/tests/test_scheduler.py | 175 ++++++++++++++++++++++++++-----------------
nova/tests/test_virt.py | 86 ++++++++++++++++++++-
nova/utils.py | 18 +++++
nova/virt/fake.py | 22 ++++--
nova/virt/libvirt_conn.py | 37 +++++++--
nova/virt/xenapi_conn.py | 22 ++++--
13 files changed, 476 insertions(+), 171 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 1ad3120b8..2831e273e 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -499,50 +499,6 @@ class InstanceCommands(object):
print msg
-class HostCommands(object):
- """Class for mangaging host(physical nodes)."""
-
- def list(self):
- """describe host list."""
-
- # To supress msg: No handlers could be found for logger "amqplib"
- logging.basicConfig()
-
- service_refs = db.service_get_all(context.get_admin_context())
- hosts = [h['host'] for h in service_refs]
- hosts = list(set(hosts))
- for host in hosts:
- print host
-
- def show(self, host):
- """describe cpu/memory/hdd info for host."""
-
- result = rpc.call(context.get_admin_context(),
- FLAGS.scheduler_topic,
- {"method": "show_host_resource",
- "args": {"host": host}})
-
- # Checking result msg format is necessary, that will have done
- # when this feture is included in API.
- if type(result) != dict:
- print 'Unexpected error occurs'
- elif not result['ret']:
- print '%s' % result['msg']
- else:
- cpu = result['phy_resource']['vcpus']
- mem = result['phy_resource']['memory_mb']
- hdd = result['phy_resource']['local_gb']
-
- print 'HOST\t\tPROJECT\t\tcpu\tmem(mb)\tdisk(gb)'
- print '%s\t\t\t%s\t%s\t%s' % (host, cpu, mem, hdd)
- for p_id, val in result['usage'].items():
- print '%s\t%s\t\t%s\t%s\t%s' % (host,
- p_id,
- val['vcpus'],
- val['memory_mb'],
- val['local_gb'])
-
-
class ServiceCommands(object):
"""Enable and disable running services"""
@@ -587,6 +543,53 @@ class ServiceCommands(object):
return
db.service_update(ctxt, svc['id'], {'disabled': True})
+ def describeresource(self, host):
+ """describe cpu/memory/hdd info for host."""
+
+ result = rpc.call(context.get_admin_context(),
+ FLAGS.scheduler_topic,
+ {"method": "show_host_resource",
+ "args": {"host": host}})
+
+ # Checking result msg format is necessary, that will have done
+ # when this feture is included in API.
+ if type(result) != dict:
+ print 'Unexpected error occurs'
+ elif not result['ret']:
+ print '%s' % result['msg']
+ else:
+ cpu = result['phy_resource']['vcpus']
+ mem = result['phy_resource']['memory_mb']
+ hdd = result['phy_resource']['local_gb']
+ cpu_u = result['phy_resource']['vcpus_used']
+ mem_u = result['phy_resource']['memory_mb_used']
+ hdd_u = result['phy_resource']['local_gb_used']
+
+ print 'HOST\t\t\tPROJECT\t\tcpu\tmem(mb)\tdisk(gb)'
+ print '%s(total)\t\t\t%s\t%s\t%s' % (host, cpu, mem, hdd)
+ print '%s(used)\t\t\t%s\t%s\t%s' % (host, cpu_u, mem_u, hdd_u)
+ for p_id, val in result['usage'].items():
+ print '%s\t\t%s\t\t%s\t%s\t%s' % (host,
+ p_id,
+ val['vcpus'],
+ val['memory_mb'],
+ val['local_gb'])
+
+ def updateresource(self, host):
+ """update available vcpu/memory/disk info for host."""
+
+ ctxt = context.get_admin_context()
+ service_refs = db.service_get_all_by_host(ctxt, host)
+ if len(service_refs) <= 0:
+ raise exception.Invalid(_('%s does not exists.') % host)
+
+ service_refs = [s for s in service_refs if s['topic'] == 'compute']
+ if len(service_refs) <= 0:
+ raise exception.Invalid(_('%s is not compute node.') % host)
+
+ result = rpc.call(ctxt, db.queue_get_for(ctxt, FLAGS.compute_topic, host),
+ {"method": "update_available_resource"})
+
class LogCommands(object):
def request(self, request_id, logfile='/var/log/nova.log'):
@@ -606,7 +609,6 @@ CATEGORIES = [
('floating', FloatingIpCommands),
('network', NetworkCommands),
('instance', InstanceCommands),
- ('host', HostCommands),
('service', ServiceCommands),
('log', LogCommands)]
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 4acba7153..e3c5d24b6 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -134,9 +134,12 @@ class ComputeManager(manager.Manager):
raise exception.Invalid(msg)
# Updating host information
- vcpu = self.driver.get_vcpu_number()
- memory_mb = self.driver.get_memory_mb()
- local_gb = self.driver.get_local_gb()
+ vcpu = self.driver.get_vcpu_total()
+ memory_mb = self.driver.get_memory_mb_total()
+ local_gb = self.driver.get_local_gb_total()
+ vcpu_u = self.driver.get_vcpu_used()
+ memory_mb_u = self.driver.get_memory_mb_used()
+ local_gb_u = self.driver.get_local_gb_used()
hypervisor = self.driver.get_hypervisor_type()
version = self.driver.get_hypervisor_version()
cpu_info = self.driver.get_cpu_info()
@@ -146,10 +149,42 @@ class ComputeManager(manager.Manager):
{'vcpus': vcpu,
'memory_mb': memory_mb,
'local_gb': local_gb,
+ 'vcpus_used':vcpu_u,
+ 'memory_mb_used': memory_mb_u,
+ 'local_gb_used': local_gb_u,
'hypervisor_type': hypervisor,
'hypervisor_version': version,
'cpu_info': cpu_info})
+ def update_available_resource(self, context):
+ """
+ update compute node specific info to DB.
+ Alghough this might be subset of update_service,
+ udpate_service() is used only nova-compute is lauched.
+ On the other hand, this method is used whenever administrators
+ request comes.
+ """
+ try:
+ service_ref = self.db.service_get_by_args(context,
+ self.host,
+ 'nova-compute')
+ except exception.NotFound:
+ msg = _(("""Cannot update resource info."""
+ """ Because no service record found."""))
+ raise exception.Invalid(msg)
+
+ # Updating host information
+ vcpu_u = self.driver.get_vcpu_used()
+ memory_mb_u = self.driver.get_memory_mb_used()
+ local_gb_u = self.driver.get_local_gb_used()
+
+ self.db.service_update(context,
+ service_ref['id'],
+ {'vcpus_used':vcpu_u,
+ 'memory_mb_used': memory_mb_u,
+ 'local_gb_used': local_gb_u})
+ return
+
def _update_state(self, context, instance_id):
"""Update the state of an instance from the driver info."""
# FIXME(ja): include other fields from state?
@@ -596,6 +631,19 @@ class ComputeManager(manager.Manager):
""" Check the host cpu is compatible to a cpu given by xml."""
return self.driver.compare_cpu(cpu_info)
+ def mktmpfile(self, context):
+ """make tmpfile under FLAGS.instance_path."""
+ return utils.mktmpfile(FLAGS.instances_path)
+
+ def exists(self, context, path):
+ """Confirm existence of the tmpfile given by path."""
+ if not utils.exists(path):
+ raise exception.NotFound(_('%s not found') % path)
+
+ def remove(self, context, path):
+ """remove the tmpfile given by path."""
+ return utils.remove(path)
+
def pre_live_migration(self, context, instance_id):
"""Any preparation for live migration at dst host."""
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 7c40d5596..217b14bf7 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -164,7 +164,10 @@ class Service(BASE, NovaBase):
vcpus = Column(Integer, nullable=True)
memory_mb = Column(Integer, nullable=True)
local_gb = Column(Integer, nullable=True)
- hypervisor_type = Column(String(128), nullable=True)
+ vcpus_used = Column(Integer, nullable=True)
+ memory_mb_used = Column(Integer, nullable=True)
+ local_gb_used = Column(Integer, nullable=True)
+ hypervisor_type = Column(Text(), nullable=True)
hypervisor_version = Column(Integer, nullable=True)
# Note(masumotok): Expected Strings example:
#
diff --git a/nova/rpc.py b/nova/rpc.py
index 49b11602b..cf4004079 100644
--- a/nova/rpc.py
+++ b/nova/rpc.py
@@ -250,6 +250,9 @@ def msg_reply(msg_id, reply=None, failure=None):
try:
publisher.send({'result': reply, 'failure': failure})
except TypeError:
+ print '>>>>>>>>>>>>>>>>>>'
+ print reply
+ print '>>>>>>>>>>>>>>>>>>'
publisher.send(
{'result': dict((k, repr(v))
for k, v in reply.__dict__.iteritems()),
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index d4ad42388..937f09c6f 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -33,6 +33,7 @@ from nova.compute import power_state
FLAGS = flags.FLAGS
flags.DEFINE_integer('service_down_time', 60,
'maximum time since last checkin for up service')
+flags.DECLARE('instances_path', 'nova.compute.manager')
class NoValidHost(exception.Error):
@@ -163,6 +164,8 @@ class Scheduler(object):
http://wiki.libvirt.org/page/TodoPreMigrationChecks
"""
+ # Checking shared storage connectivity
+ self.mounted_on_same_shared_storage(context, instance_ref, dest)
# Checking dest exists.
dservice_refs = db.service_get_all_by_host(context, dest)
@@ -207,38 +210,60 @@ class Scheduler(object):
raise e
def has_enough_resource(self, context, instance_ref, dest):
- """Check if destination host has enough resource for live migration"""
+ """
+ Check if destination host has enough resource for live migration.
+ Currently, only memory checking has been done.
+ If storage migration(block migration, meaning live-migration
+ without any shared storage) will be available, local storage
+ checking is also necessary.
+ """
# Getting instance information
ec2_id = instance_ref['hostname']
- vcpus = instance_ref['vcpus']
mem = instance_ref['memory_mb']
- hdd = instance_ref['local_gb']
- # Gettin host information
+ # Getting host information
service_refs = db.service_get_all_by_host(context, dest)
if len(service_refs) <= 0:
raise exception.Invalid(_('%s does not exists.') % dest)
service_ref = service_refs[0]
- total_cpu = int(service_ref['vcpus'])
- total_mem = int(service_ref['memory_mb'])
- total_hdd = int(service_ref['local_gb'])
+ mem_total = int(service_ref['memory_mb'])
+ mem_used = int(service_ref['memory_mb_used'])
+ mem_avail = mem_total - mem_used
+ mem_inst = instance_ref['memory_mb']
+ if mem_avail <= mem_inst:
+ msg = _('%s is not capable to migrate %s(host:%s <= instance:%s)')
+ raise exception.NotEmpty(msg % (dest, ec2_id, mem_avail, mem_inst))
- instances_refs = db.instance_get_all_by_host(context, dest)
- for i_ref in instances_refs:
- total_cpu -= int(i_ref['vcpus'])
- total_mem -= int(i_ref['memory_mb'])
- total_hdd -= int(i_ref['local_gb'])
+ def mounted_on_same_shared_storage(self, context, instance_ref, dest):
+ """
+ Check if /nova-inst-dir/insntances is mounted same storage at
+ live-migration src and dest host.
+ """
+ src = instance_ref['host']
+ dst_t = db.queue_get_for(context, FLAGS.compute_topic, dest)
+ src_t = db.queue_get_for(context, FLAGS.compute_topic, src)
- # Checking host has enough information
- logging.debug(_('host(%s) remains vcpu:%s mem:%s hdd:%s,') %
- (dest, total_cpu, total_mem, total_hdd))
- logging.debug(_('instance(%s) has vcpu:%s mem:%s hdd:%s,') %
- (ec2_id, vcpus, mem, hdd))
+ # create tmpfile at dest host
+ try:
+ filename = rpc.call(context, dst_t, {"method": 'mktmpfile'})
+ except rpc.RemoteError, e:
+ msg = _("Cannot create tmpfile at %s to confirm shared storage.")
+ logging.error(msg % FLAGS.instance_path)
+ raise e
- if total_cpu <= vcpus or total_mem <= mem or total_hdd <= hdd:
- raise exception.NotEmpty(_('%s is not capable to migrate %s') %
- (dest, ec2_id))
+ # make sure existence at src host.
+ try:
+ rpc.call(context, src_t,
+ {"method": 'exists', "args":{'path':filename}})
+
+ except (rpc.RemoteError, exception.NotFound), e:
+ msg = (_("""Cannot comfirm %s at %s to confirm shared storage."""
+ """Check if %s is same shared storage"""))
+ logging.error(msg % FLAGS.instance_path)
+ raise e
- logging.debug(_('%s has_enough_resource() for %s') % (dest, ec2_id))
+ # then remove.
+ rpc.call(context, dst_t,
+ {"method": 'remove', "args":{'path':filename}})
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index a181225a6..b40f46a85 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -84,7 +84,10 @@ class SchedulerManager(manager.Manager):
# Getting physical resource information
h_resource = {'vcpus': service_ref['vcpus'],
'memory_mb': service_ref['memory_mb'],
- 'local_gb': service_ref['local_gb']}
+ 'local_gb': service_ref['local_gb'],
+ 'vcpus_used': service_ref['vcpus_used'],
+ 'memory_mb_used': service_ref['memory_mb_used'],
+ 'local_gb_used': service_ref['local_gb_used']}
# Getting usage resource information
u_resource = {}
@@ -108,8 +111,8 @@ class SchedulerManager(manager.Manager):
hdd = db.instance_get_disk_sum_by_host_and_project(context,
host,
p_id)
- u_resource[p_id] = {'vcpus': vcpus,
- 'memory_mb': mem,
- 'local_gb': hdd}
+ u_resource[p_id] = {'vcpus': int(vcpus),
+ 'memory_mb': int(mem),
+ 'local_gb': int(hdd)}
return {'ret': True, 'phy_resource': h_resource, 'usage': u_resource}
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 344c2d2b5..8d3ac315d 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -268,7 +268,8 @@ class ComputeTestCase(test.TestCase):
"""
def dic_key_check(dic):
- validkey = ['vcpus', 'memory_mb', 'local_gb',
+ validkey = ['vcpus', 'memory_mb', 'local_gb',
+ 'vcpus_used', 'memory_mb_used', 'local_gb_used',
'hypervisor_type', 'hypervisor_version', 'cpu_info']
return (list(set(validkey)) == list(set(dic.keys())))
@@ -286,13 +287,55 @@ class ComputeTestCase(test.TestCase):
self.compute.db = dbmock
self.mox.ReplayAll()
+ self.compute.update_service('dummy', host, binary)
+ self.mox.ResetAll()
+
+ def test_update_available_resource_exception(self):
+ """a testcase of update_available_resource raises exception"""
+ host = 'foo'
+ binary = 'nova-compute'
+ ctxt = context.get_admin_context()
+ dbmock = self.mox.CreateMock(db)
+ dbmock.service_get_by_args(mox.IgnoreArg(),
+ mox.StrContains(host),
+ mox.StrContains(binary)).\
+ AndRaise(exception.NotFound())
+ self.compute.db = dbmock
+ self.compute.host = host
+ self.mox.ReplayAll()
try:
- self.compute.update_service('dummy', host, binary)
+ self.compute.update_available_resource(ctxt)
except exception.Invalid, e:
- msg = 'Cannot insert compute manager specific info'
+ msg = 'Cannot update resource info.'
c1 = ( 0 <= e.message.find(msg))
self.assertTrue(c1)
- self.mox.ResetAll()
+ self.mox.UnsetStubs()
+
+ def test_update_available_resource_success(self):
+ """a testcase of update_available_resource finishes with no errors"""
+
+ def dic_key_check(dic):
+ validkey = [ 'vcpus_avail', 'memory_mb_avail', 'local_gb_avail']
+ return (list(set(validkey)) == list(set(dic.keys())))
+
+ host = 'foo'
+ binary = 'nova-compute'
+ ctxt = context.get_admin_context()
+ service_ref = {'id':1, 'binary':'nova-compute', 'topic':'compute'}
+ dbmock = self.mox.CreateMock(db)
+ dbmock.service_get_by_args(mox.IgnoreArg(),
+ mox.StrContains(host),
+ mox.StrContains(binary)).\
+ AndReturn(service_ref)
+ dbmock.service_update(mox.IgnoreArg(),
+ service_ref['id'],
+ mox.Func(dic_key_check))
+
+ self.compute.db = dbmock
+ self.compute.host = host
+ self.mox.ReplayAll()
+ self.compute.update_available_resource(ctxt)
+ self.mox.UnsetStubs()
def _setup_other_managers(self):
self.volume_manager = utils.import_object(FLAGS.volume_manager)
@@ -444,7 +487,7 @@ class ComputeTestCase(test.TestCase):
rpc.call(c, db.queue_get_for(c, FLAGS.compute_topic, dest),
{"method": "pre_live_migration",
"args": {'instance_id': i_id}}).\
- InAnyOrder('g1').AndRaise(rpc.RemoteError('du', 'mm', 'y'))
+ InAnyOrder('g1').AndRaise(rpc.RemoteError('', '', ''))
self.mox.StubOutWithMock(compute_manager.LOG, 'error')
compute_manager.LOG.error('Pre live migration for %s failed at %s',
instance_ref['hostname'], dest)
@@ -480,7 +523,7 @@ class ComputeTestCase(test.TestCase):
rpc.call(c, compute_topic,
{"method": "pre_live_migration",
"args": {'instance_id': i_id}}).\
- AndRaise(rpc.RemoteError('du', 'mm', 'y'))
+ AndRaise(rpc.RemoteError('', '', ''))
self.mox.StubOutWithMock(compute_manager.LOG, 'error')
compute_manager.LOG.error('Pre live migration for %s failed at %s',
instance_ref['hostname'], dest)
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index c62bca9b1..36d99d666 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -41,6 +41,7 @@ from nova.db.sqlalchemy import models
FLAGS = flags.FLAGS
flags.DECLARE('max_cores', 'nova.scheduler.simple')
flags.DECLARE('stub_network', 'nova.compute.manager')
+flags.DECLARE('instances_path', 'nova.compute.manager')
class TestDriver(driver.Scheduler):
@@ -111,7 +112,8 @@ class SchedulerTestCase(test.TestCase):
scheduler = manager.SchedulerManager()
dest = 'dummydest'
ctxt = context.get_admin_context()
- r0 = {'vcpus':16, 'memory_mb':32, 'local_gb':100}
+ r0 = {'vcpus':16, 'memory_mb':32, 'local_gb':100,
+ 'vcpus_used':16, 'memory_mb_used':32, 'local_gb_used':10}
service_ref = {'id':1, 'host':dest}
service_ref.update(r0)
@@ -140,7 +142,8 @@ class SchedulerTestCase(test.TestCase):
scheduler = manager.SchedulerManager()
dest = 'dummydest'
ctxt = context.get_admin_context()
- r0 = {'vcpus':16, 'memory_mb':32, 'local_gb':100}
+ r0 = {'vcpus':16, 'memory_mb':32, 'local_gb':100,
+ 'vcpus_used':16, 'memory_mb_used':32, 'local_gb_used':10}
r1 = {'vcpus':10, 'memory_mb':4, 'local_gb':20}
r2 = {'vcpus':10, 'memory_mb':20, 'local_gb':30}
service_ref = {'id':1, 'host':dest}
@@ -148,7 +151,7 @@ class SchedulerTestCase(test.TestCase):
instance_ref2 = {'id':2, 'project_id':'p-01', 'host':'dummy'}
instance_ref2.update(r1)
instance_ref3 = {'id':3, 'project_id':'p-02', 'host':'dummy'}
- instance_ref3.update(r1)
+ instance_ref3.update(r2)
self.mox.StubOutWithMock(manager, 'db', use_mock_anything=True)
manager.db.service_get_all_compute_sorted(mox.IgnoreArg()).\
@@ -176,6 +179,7 @@ class SchedulerTestCase(test.TestCase):
self.assertTrue( c1 and c2 and c3 and c4 and c5 and c6)
self.mox.UnsetStubs()
+
class ZoneSchedulerTestCase(test.TestCase):
"""Test case for zone scheduler"""
def setUp(self):
@@ -495,7 +499,7 @@ class SimpleDriverTestCase(test.TestCase):
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
i_ref = {'id':1, 'hostname':'i-00000001', 'host':'dummy',
- 'volumes':[{'id':1}, {'id':2}]}
+ 'volumes':[{'id':1}, {'id':2}]}
dest = 'dummydest'
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
@@ -793,7 +797,10 @@ class SimpleDriverTestCase(test.TestCase):
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy'}
+ driver_i = self.scheduler.driver
+ self.mox.StubOutWithMock(driver_i, 'mounted_on_same_shared_storage')
+ driver_i.mounted_on_same_shared_storage(mox.IgnoreArg(), i_ref, dest)
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
AndReturn([])
@@ -813,6 +820,7 @@ class SimpleDriverTestCase(test.TestCase):
Original host(an instance launched on) does not exist.
"""
dest = 'dummydest'
+ driver_i = self.scheduler.driver
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy', 'launched_on':'h1'}
@@ -821,6 +829,8 @@ class SimpleDriverTestCase(test.TestCase):
service_ref.__setitem__('topic', 'compute')
service_ref.__setitem__('host', i_ref['host'])
+ self.mox.StubOutWithMock(driver_i, 'mounted_on_same_shared_storage')
+ driver_i.mounted_on_same_shared_storage(mox.IgnoreArg(), i_ref, dest)
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
AndReturn([service_ref])
@@ -844,6 +854,7 @@ class SimpleDriverTestCase(test.TestCase):
Original host and dest host has different hypervisor type.
"""
dest = 'dummydest'
+ driver_i = self.scheduler.driver
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
i_ref = {'id':1, 'hostname':'i-01',
@@ -856,6 +867,8 @@ class SimpleDriverTestCase(test.TestCase):
service_ref2.__setitem__('id', 2)
service_ref2.__setitem__('hypervisor_type', 'xen')
+ self.mox.StubOutWithMock(driver_i, 'mounted_on_same_shared_storage')
+ driver_i.mounted_on_same_shared_storage(mox.IgnoreArg(), i_ref, dest)
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
AndReturn([service_ref])
@@ -879,6 +892,7 @@ class SimpleDriverTestCase(test.TestCase):
Original host and dest host has different hypervisor version.
"""
dest = 'dummydest'
+ driver_i = self.scheduler.driver
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
i_ref = {'id':1, 'hostname':'i-01',
@@ -891,6 +905,8 @@ class SimpleDriverTestCase(test.TestCase):
service_ref2.__setitem__('id', 2)
service_ref2.__setitem__('hypervisor_version', 12001)
+ self.mox.StubOutWithMock(driver_i, 'mounted_on_same_shared_storage')
+ driver_i.mounted_on_same_shared_storage(mox.IgnoreArg(), i_ref, dest)
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
AndReturn([service_ref])
@@ -914,6 +930,7 @@ class SimpleDriverTestCase(test.TestCase):
Original host and dest host has different hypervisor version.
"""
dest = 'dummydest'
+ driver_i = self.scheduler.driver
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
i_ref = {'id':1, 'hostname':'i-01',
@@ -927,6 +944,8 @@ class SimpleDriverTestCase(test.TestCase):
service_ref2.__setitem__('hypervisor_version', 12000)
service_ref2.__setitem__('cpuinfo', 'info')
+ self.mox.StubOutWithMock(driver_i, 'mounted_on_same_shared_storage')
+ driver_i.mounted_on_same_shared_storage(mox.IgnoreArg(), i_ref, dest)
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
AndReturn([service_ref])
@@ -956,6 +975,7 @@ class SimpleDriverTestCase(test.TestCase):
The testcase make sure everything finished with no error.
"""
dest = 'dummydest'
+ driver_i = self.scheduler.driver
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
i_ref = {'id':1, 'hostname':'i-01',
@@ -969,6 +989,8 @@ class SimpleDriverTestCase(test.TestCase):
service_ref2.__setitem__('hypervisor_version', 12000)
service_ref2.__setitem__('cpuinfo', 'info')
+ self.mox.StubOutWithMock(driver_i, 'mounted_on_same_shared_storage')
+ driver_i.mounted_on_same_shared_storage(mox.IgnoreArg(), i_ref, dest)
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
AndReturn([service_ref])
@@ -988,121 +1010,134 @@ class SimpleDriverTestCase(test.TestCase):
self.assertTrue(ret == None)
self.mox.UnsetStubs()
- def test_has_enough_resource_lack_resource_vcpu(self):
+ def test_has_enough_resource_lack_resource_memory(self):
"""
A testcase of driver.has_enough_resource.
- Lack of vcpu.(boundary check)
+ Lack of memory_mb.(boundary check)
"""
dest = 'dummydest'
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
- service_ref = {'id':1, 'vcpus':16, 'memory_mb':32, 'local_gb':100}
- i_ref1 = {'id':1, 'hostname':'i-01', 'host':'dummy',
- 'vcpus':6, 'memory_mb':8, 'local_gb':10}
- i_ref2 = {'id':2, 'hostname':'i-01', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':10}
- i_ref3 = {'id':3, 'hostname':'i-02', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+ service_ref = {'id':1, 'memory_mb':32, 'memory_mb_used':12, 'local_gb':100}
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy',
+ 'vcpus':5, 'memory_mb':20, 'local_gb':10}
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
AndReturn([service_ref])
- driver.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
- AndReturn([i_ref2, i_ref3])
self.mox.ReplayAll()
try:
- self.scheduler.driver.has_enough_resource(ctxt, i_ref1, dest)
+ self.scheduler.driver.has_enough_resource(ctxt, i_ref, dest)
except exception.NotEmpty, e:
msg = 'is not capable to migrate'
self.assertTrue(e.message.find(msg) >= 0)
self.mox.UnsetStubs()
+ self.mox.UnsetStubs()
- def test_has_enough_resource_lack_resource_memory(self):
+ def test_has_enough_resource_works_correctly(self):
"""
- A testcase of driver.has_enough_resource.
- Lack of memory_mb.(boundary check)
+ A testcase of driver.has_enough_resource
+ to make sure everything finished with no error.
"""
dest = 'dummydest'
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
- service_ref = {'id':1, 'vcpus':16, 'memory_mb':32, 'local_gb':100}
- i_ref1 = {'id':1, 'hostname':'i-01', 'host':'dummy',
- 'vcpus':5, 'memory_mb':16, 'local_gb':10}
- i_ref2 = {'id':2, 'hostname':'i-01', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':10}
- i_ref3 = {'id':3, 'hostname':'i-02', 'host':'dummy',
+ service_ref = {'id':1, 'memory_mb':120, 'memory_mb_used':32}
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy',
'vcpus':5, 'memory_mb':8, 'local_gb':10}
self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
AndReturn([service_ref])
- driver.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
- AndReturn([i_ref2, i_ref3])
self.mox.ReplayAll()
- try:
- self.scheduler.driver.has_enough_resource(ctxt, i_ref1, dest)
- except exception.NotEmpty, e:
- msg = 'is not capable to migrate'
- self.assertTrue(e.message.find(msg) >= 0)
+ ret = self.scheduler.driver.has_enough_resource(ctxt, i_ref, dest)
+ self.assertTrue(ret == None)
self.mox.UnsetStubs()
+
+ def test_mounted_on_same_shared_storage_cannot_make_tmpfile(self):
+ """
+ A testcase of driver.mounted_on_same_shared_storage
+ checks log message when dest host cannot make tmpfile.
+ """
+ dest = 'dummydest'
+ driver_i = self.scheduler.driver
+ ctxt = context.get_admin_context()
+ topic = FLAGS.compute_topic
+ fpath = '/test/20110127120000'
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy'}
+
+ self.mox.StubOutWithMock(driver, 'rpc', use_mock_anything=True)
+ driver.rpc.call(mox.IgnoreArg(),
+ db.queue_get_for(ctxt, FLAGS.compute_topic, dest),
+ {"method": 'mktmpfile'}).AndRaise(rpc.RemoteError('', '', ''))
+ self.mox.StubOutWithMock(driver.logging, 'error')
+ msg = _("Cannot create tmpfile at %s to confirm shared storage.")
+ driver.logging.error(msg % FLAGS.instances_path)
+
+ self.mox.ReplayAll()
+ self.assertRaises(rpc.RemoteError,
+ driver_i.mounted_on_same_shared_storage,
+ ctxt, i_ref, dest)
self.mox.UnsetStubs()
- def test_has_enough_resource_lack_resource_disk(self):
+ def test_mounted_on_same_shared_storage_cannot_comfirm_tmpfile(self):
"""
- A testcase of driver.has_enough_resource.
- Lack of local_gb.(boundary check)
+ A testcase of driver.mounted_on_same_shared_storage
+ checks log message when src host cannot comfirm tmpfile.
"""
- scheduler = manager.SchedulerManager()
dest = 'dummydest'
+ driver_i = self.scheduler.driver
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
- service_ref = {'id':1, 'vcpus':16, 'memory_mb':32, 'local_gb':100}
- i_ref1 = {'id':1, 'hostname':'i-01', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':80}
- i_ref2 = {'id':2, 'hostname':'i-01', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':10}
- i_ref3 = {'id':3, 'hostname':'i-02', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+ fpath = '/test/20110127120000'
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy'}
+
+ self.mox.StubOutWithMock(driver, 'rpc', use_mock_anything=True)
+ driver.rpc.call(mox.IgnoreArg(),
+ db.queue_get_for(ctxt, FLAGS.compute_topic, dest),
+ {"method": 'mktmpfile'}).AndReturn(fpath)
+ driver.rpc.call(mox.IgnoreArg(),
+ db.queue_get_for(ctxt, FLAGS.compute_topic, i_ref['host']),
+ {"method": 'exists', "args":{'path':fpath}}).\
+ AndRaise(rpc.RemoteError('','',''))
+ self.mox.StubOutWithMock(driver.logging, 'error')
+ msg = _("Cannot create tmpfile at %s to confirm shared storage.")
+ driver.logging.error(msg % FLAGS.instances_path)
- self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
- driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
- AndReturn([service_ref])
- driver.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
- AndReturn([i_ref2, i_ref3])
-
self.mox.ReplayAll()
- try:
- self.scheduler.driver.has_enough_resource(ctxt, i_ref1, dest)
- except exception.NotEmpty, e:
- msg = 'is not capable to migrate'
- self.assertTrue(e.message.find(msg) >= 0)
+ self.assertRaises(rpc.RemoteError,
+ driver_i.mounted_on_same_shared_storage,
+ ctxt, i_ref, dest)
self.mox.UnsetStubs()
- def test_has_enough_resource_works_correctly(self):
+
+ def test_mounted_on_same_shared_storage_works_correctly(self):
"""
- A testcase of driver.has_enough_resource
+ A testcase of driver.mounted_on_same_shared_storage
to make sure everything finished with no error.
"""
dest = 'dummydest'
ctxt = context.get_admin_context()
topic = FLAGS.compute_topic
- service_ref = {'id':1, 'vcpus':16, 'memory_mb':32, 'local_gb':100}
- i_ref1 = {'id':1, 'hostname':'i-01', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':10}
- i_ref2 = {'id':2, 'hostname':'i-01', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':10}
- i_ref3 = {'id':3, 'hostname':'i-02', 'host':'dummy',
- 'vcpus':5, 'memory_mb':8, 'local_gb':10}
+ fpath = '/test/20110127120000'
+ i_ref = {'id':1, 'hostname':'i-01', 'host':'dummy'}
+
+ self.mox.StubOutWithMock(driver, 'rpc', use_mock_anything=True)
+ driver.rpc.call(mox.IgnoreArg(),
+ db.queue_get_for(mox.IgnoreArg(), FLAGS.compute_topic, dest),
+ {"method": 'mktmpfile'}).AndReturn(fpath)
+ driver.rpc.call(mox.IgnoreArg(),
+ db.queue_get_for(mox.IgnoreArg(), FLAGS.compute_topic, i_ref['host']),
+ {"method": 'exists', "args":{'path':fpath}})
+ driver.rpc.call(mox.IgnoreArg(),
+ db.queue_get_for(mox.IgnoreArg(), FLAGS.compute_topic, dest),
+ {"method": 'remove', "args":{'path':fpath}})
- self.mox.StubOutWithMock(driver, 'db', use_mock_anything=True)
- driver.db.service_get_all_by_host(mox.IgnoreArg(), dest).\
- AndReturn([service_ref])
- driver.db.instance_get_all_by_host(mox.IgnoreArg(), dest).\
- AndReturn([i_ref2, i_ref3])
-
self.mox.ReplayAll()
- ret = self.scheduler.driver.has_enough_resource(ctxt, i_ref1, dest)
+ ret = self.scheduler.driver.mounted_on_same_shared_storage(ctxt,
+ i_ref,
+ dest)
self.assertTrue(ret == None)
self.mox.UnsetStubs()
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index 177e8f021..2828baced 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -241,11 +241,11 @@ class LibvirtConnTestCase(test.TestCase):
uri = conn.get_uri()
self.assertEquals(uri, testuri)
- def test_get_memory_mb(self):
+ def test_get_vcpu_total(self):
"""
- Check if get_memory_mb returns memory value
+ Check if get_vcpu_total returns appropriate cpu value
Connection/OS/driver differenct does not matter for this method,
- so everyone can execute for checking.
+ everyone can execute for checking.
"""
try:
self._driver_dependent_test_setup()
@@ -254,9 +254,87 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
- self.assertTrue(0 < conn.get_memory_mb())
+ self.assertTrue(0 < conn.get_vcpu_total())
self.mox.UnsetStubs()
+
+ def test_get_memory_mb_total(self):
+ """Check if get_memory_mb returns appropriate memory value"""
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertTrue(0 < conn.get_memory_mb_total())
+ self.mox.UnsetStubs()
+
+ def test_get_local_gb_total(self):
+ """Check if get_local_gb_total returns appropriate disk value"""
+ # Note(masumotok): cannot test b/c FLAGS.instances_path is
+ # inevitable for this test..
+ #try:
+ # self._driver_dependent_test_setup()
+ #except:
+ # return
+ #
+ #self.mox.ReplayAll()
+ #conn = libvirt_conn.LibvirtConnection(False)
+ #self.assertTrue(0 < conn.get_local_gb_total())
+ #self.mox.UnsetStubs()
+ pass
+
+ def test_get_vcpu_used(self):
+ """Check if get_local_gb_total returns appropriate disk value"""
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn', use_mock_anything=True)
+ libvirt_conn.LibvirtConnection._conn.listDomainsID().AndReturn([1,2])
+ vdmock = self.mox.CreateMock(libvirt.virDomain)
+ self.mox.StubOutWithMock(vdmock, "vcpus", use_mock_anything=True)
+ vdmock.vcpus().AndReturn(['', [('dummycpu'), ('dummycpu')]])
+ vdmock.vcpus().AndReturn(['', [('dummycpu'), ('dummycpu')]])
+ libvirt_conn.LibvirtConnection._conn.lookupByID(mox.IgnoreArg()).\
+ AndReturn(vdmock)
+ libvirt_conn.LibvirtConnection._conn.lookupByID(mox.IgnoreArg()).\
+ AndReturn(vdmock)
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertTrue( conn.get_vcpu_used() == 4)
+ self.mox.UnsetStubs()
+
+ def test_get_memory_mb_used(self):
+ """Check if get_memory_mb returns appropriate memory value"""
+ try:
+ self._driver_dependent_test_setup()
+ except:
+ return
+
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertTrue(0 < conn.get_memory_mb_used())
+ self.mox.UnsetStubs()
+
+ def test_get_local_gb_used(self):
+ """Check if get_local_gb_total returns appropriate disk value"""
+ # Note(masumotok): cannot test b/c FLAGS.instances_path is
+ # inevitable for this test..
+ #try:
+ # self._driver_dependent_test_setup()
+ #except:
+ # return
+
+ #self.mox.ReplayAll()
+ #conn = libvirt_conn.LibvirtConnection(False)
+ #self.assertTrue(0 < conn.get_local_gb_used())
+ #self.mox.UnsetStubs()
+ pass
+
def test_get_cpu_info_works_correctly(self):
"""
Check if get_cpu_info works correctly.
diff --git a/nova/utils.py b/nova/utils.py
index 6d3ddd092..e8919d7a5 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -303,6 +303,24 @@ def str_dict_replace(s, mapping):
return s
+def mktmpfile(dir):
+ """create tmpfile under dir, and return filename."""
+ filename = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S')
+ fpath = os.path.join(dir, filename)
+ open(fpath, 'a+').write(fpath+'\n')
+ return fpath
+
+
+def exists(filename):
+ """check file path existence."""
+ return os.path.exists(filename)
+
+
+def remove(filename):
+ """remove file."""
+ return os.remove(filename)
+
+
class LazyPluggable(object):
"""A pluggable backend loaded lazily based on some value."""
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index f469af681..4bf477f5b 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -314,16 +314,28 @@ class FakeConnection(object):
"""This method is supported only libvirt. """
return
- def get_vcpu_number(self):
+ def get_vcpu_total(self):
"""This method is supported only libvirt. """
return
- def get_memory_mb(self):
- """This method is supported only libvirt.."""
+ def get_memory_mb_total(self):
+ """This method is supported only libvirt. """
return
- def get_local_gb(self):
- """This method is supported only libvirt.."""
+ def get_local_gb_total(self):
+ """This method is supported only libvirt. """
+ return
+
+ def get_vcpu_used(self):
+ """This method is supported only libvirt. """
+ return
+
+ def get_memory_mb_used(self):
+ """This method is supported only libvirt. """
+ return
+
+ def get_local_gb_used(self):
+ """This method is supported only libvirt. """
return
def get_hypervisor_type(self):
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 49dd03c57..aefa32dcb 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -835,21 +835,44 @@ class LibvirtConnection(object):
return interfaces
- def get_vcpu_number(self):
+ def get_vcpu_total(self):
""" Get vcpu number of physical computer. """
- return self._conn.getMaxVcpus(None)
+ return open('/proc/cpuinfo').read().count('processor')
- def get_memory_mb(self):
- """Get the memory size of physical computer ."""
+ def get_memory_mb_total(self):
+ """Get the total memory size(MB) of physical computer ."""
meminfo = open('/proc/meminfo').read().split()
idx = meminfo.index('MemTotal:')
# transforming kb to mb.
return int(meminfo[idx + 1]) / 1024
- def get_local_gb(self):
- """Get the hdd size of physical computer ."""
+ def get_local_gb_total(self):
+ """Get the total hdd size(GB) of physical computer ."""
hddinfo = os.statvfs(FLAGS.instances_path)
- return hddinfo.f_bsize * hddinfo.f_blocks / 1024 / 1024 / 1024
+ return hddinfo.f_frsize * hddinfo.f_blocks / 1024 / 1024 / 1024
+
+ def get_vcpu_used(self):
+ """ Get vcpu available number of physical computer. """
+ total = 0
+ for i in self._conn.listDomainsID():
+ dom = self._conn.lookupByID(i)
+ total += len(dom.vcpus()[1])
+ return total
+
+ def get_memory_mb_used(self):
+ """Get the free memory size(MB) of physical computer."""
+ m = open('/proc/meminfo').read().split()
+ idx1 = m.index('MemFree:')
+ idx2 = m.index('Buffers:')
+ idx3 = m.index('Cached:')
+ avail = (int(m[idx1+1]) + int(m[idx2+1]) + int(m[idx3+1])) / 1024
+ return self.get_memory_mb_total() - avail
+
+ def get_local_gb_used(self):
+ """Get the free hdd size(GB) of physical computer ."""
+ hddinfo = os.statvfs(FLAGS.instances_path)
+ avail = hddinfo.f_frsize * hddinfo.f_bavail / 1024 / 1024 / 1024
+ return self.get_local_gb_total() - avail
def get_hypervisor_type(self):
""" Get hypervisor type """
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 1e7933f51..902879d09 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -213,16 +213,28 @@ class XenAPIConnection(object):
"""This method is supported only libvirt. """
return
- def get_vcpu_number(self):
+ def get_vcpu_total(self):
"""This method is supported only libvirt. """
return
- def get_memory_mb(self):
- """This method is supported only libvirt.."""
+ def get_memory_mb_total(self):
+ """This method is supported only libvirt. """
return
- def get_local_gb(self):
- """This method is supported only libvirt.."""
+ def get_local_gb_total(self):
+ """This method is supported only libvirt. """
+ return
+
+ def get_vcpu_used(self):
+ """This method is supported only libvirt. """
+ return
+
+ def get_memory_mb_used(self):
+ """This method is supported only libvirt. """
+ return
+
+ def get_local_gb_used(self):
+ """This method is supported only libvirt. """
return
def get_hypervisor_type(self):
--
cgit
From d88d74c9a0a28e0ebd6cedf694753b9ee9decdac Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Fri, 18 Feb 2011 14:15:04 +0900
Subject: fixed based on reviewer's comment. 1. erase wrapper
function(remove/exists/mktempfile) from nova.utils. 2. nova-manage service
describeresource(->describe_resource) 3. nova-manage service
updateresource(->update_resource) 4. erase "my mistake print" statement
Additional changes are made at:
1. nova.image.s3.show
2. nova.compute.api.create
that's because instances cannot launched without this changes.
---
bin/nova-manage | 10 +++++-----
nova/compute/api.py | 4 ++--
nova/compute/manager.py | 15 ++++++++++-----
nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py | 9 ---------
nova/image/s3.py | 2 +-
nova/scheduler/manager.py | 1 -
nova/utils.py | 18 ------------------
nova/virt/disk.py | 1 -
nova/virt/libvirt_conn.py | 2 +-
9 files changed, 19 insertions(+), 43 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 7336a582b..0bfe0d969 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -574,7 +574,7 @@ class ServiceCommands(object):
return
db.service_update(ctxt, svc['id'], {'disabled': True})
- def describeresource(self, host):
+ def describe_resource(self, host):
"""describe cpu/memory/hdd info for host."""
result = rpc.call(context.get_admin_context(),
@@ -606,7 +606,7 @@ class ServiceCommands(object):
val['memory_mb'],
val['local_gb'])
- def updateresource(self, host):
+ def update_resource(self, host):
"""update available vcpu/memory/disk info for host."""
ctxt = context.get_admin_context()
@@ -618,9 +618,9 @@ class ServiceCommands(object):
if len(service_refs) <= 0:
raise exception.Invalid(_('%s is not compute node.') % host)
- result = rpc.call(ctxt,
- db.queue_get_for(ctxt, FLAGS.compute_topic, host),
- {"method": "update_available_resource"})
+ rpc.call(ctxt,
+ db.queue_get_for(ctxt, FLAGS.compute_topic, host),
+ {"method": "update_available_resource"})
class LogCommands(object):
diff --git a/nova/compute/api.py b/nova/compute/api.py
index ac02dbcfa..740dd3935 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -103,9 +103,9 @@ class API(base.Base):
if not is_vpn:
image = self.image_service.show(context, image_id)
if kernel_id is None:
- kernel_id = image.get('kernelId', None)
+ kernel_id = image.get('kernel_id', None)
if ramdisk_id is None:
- ramdisk_id = image.get('ramdiskId', None)
+ ramdisk_id = image.get('ramdisk_id', None)
# No kernel and ramdisk for raw images
if kernel_id == str(FLAGS.null_kernel):
kernel_id = None
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index cae95dd93..47dd5fd5e 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -38,6 +38,8 @@ import datetime
import random
import string
import socket
+import os
+import tempfile
import time
import functools
@@ -577,14 +579,17 @@ class ComputeManager(manager.Manager):
@exception.wrap_exception
def mktmpfile(self, context):
"""make tmpfile under FLAGS.instance_path."""
- return utils.mktmpfile(FLAGS.instances_path)
+ fd, name = tempfile.mkstemp(dir=FLAGS.instances_path)
+ # No essential reason to write dateinfo. just for debugging reason.
+ os.fdopen(fd, 'w').write(str(datetime.datetime.utcnow()))
+ return name
@exception.wrap_exception
def confirm_tmpfile(self, context, path):
"""Confirm existence of the tmpfile given by path."""
- if not utils.exists(path):
+ if not os.path.exists(path):
raise exception.NotFound(_('%s not found') % path)
- return utils.remove(path)
+ return os.remove(path)
@exception.wrap_exception
def update_available_resource(self, context):
@@ -683,7 +688,7 @@ class ComputeManager(manager.Manager):
Post operations for live migration.
Mainly, database updating.
"""
- LOG.info('post_live_migration() is started..')
+ LOG.info(_('post_live_migration() is started..'))
instance_id = instance_ref['id']
# Detaching volumes.
@@ -705,7 +710,7 @@ class ComputeManager(manager.Manager):
# Not return if fixed_ip is not found, otherwise,
# instance never be accessible..
if None == fixed_ip:
- logging.warn('fixed_ip is not found for %s ' % i_name)
+ LOG.warn(_('fixed_ip is not found for %s.') % i_name)
self.db.fixed_ip_update(ctxt, fixed_ip, {'host': dest})
try:
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
index 38210db85..699b837f8 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/002_bexar.py
@@ -229,12 +229,3 @@ def upgrade(migrate_engine):
networks.create_column(networks_cidr_v6)
networks.create_column(networks_ra_server)
services.create_column(services_availability_zone)
- #services.create_column(services_vcpus)
- #services.create_column(services_memory_mb)
- #services.create_column(services_local_gb)
- #services.create_column(services_vcpus_used)
- #services.create_column(services_memory_mb_used)
- #services.create_column(services_local_gb_used)
- #services.create_column(services_hypervisor_type)
- #services.create_column(services_hypervisor_version)
- #services.create_column(services_cpu_info)
diff --git a/nova/image/s3.py b/nova/image/s3.py
index 71304cdd6..14135a1ee 100644
--- a/nova/image/s3.py
+++ b/nova/image/s3.py
@@ -94,7 +94,7 @@ class S3ImageService(service.BaseImageService):
if FLAGS.connection_type == 'fake':
return {'imageId': 'bar'}
result = self.index(context)
- result = [i for i in result if i['imageId'] == image_id]
+ result = [i for i in result if i['id'] == image_id]
if not result:
raise exception.NotFound(_('Image %s could not be found')
% image_id)
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index af54c72be..ea7ae7bd2 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -60,7 +60,6 @@ class SchedulerManager(manager.Manager):
host = getattr(self.driver, driver_method)(elevated, *args,
**kwargs)
except AttributeError, e:
- print 'manager.attrerr', e
host = self.driver.schedule(elevated, topic, *args, **kwargs)
rpc.cast(context,
diff --git a/nova/utils.py b/nova/utils.py
index 966dde667..8d7ff1f64 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -337,24 +337,6 @@ def str_dict_replace(s, mapping):
return s
-def mktmpfile(dir):
- """create tmpfile under dir, and return filename."""
- filename = datetime.datetime.utcnow().strftime('%Y%m%d%H%M%S')
- fpath = os.path.join(dir, filename)
- open(fpath, 'a+').write(fpath + '\n')
- return fpath
-
-
-def exists(filename):
- """check file path existence."""
- return os.path.exists(filename)
-
-
-def remove(filename):
- """remove file."""
- return os.remove(filename)
-
-
class LazyPluggable(object):
"""A pluggable backend loaded lazily based on some value."""
diff --git a/nova/virt/disk.py b/nova/virt/disk.py
index ec4acc452..c5565abfa 100644
--- a/nova/virt/disk.py
+++ b/nova/virt/disk.py
@@ -112,7 +112,6 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False):
def _link_device(image, nbd):
"""Link image to device using loopback or nbd"""
- print '_link_device:0:', nbd, '::', image
if nbd:
device = _allocate_device()
utils.execute('sudo qemu-nbd -c %s %s' % (device, image))
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 9b7a9ddbe..579c4593e 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -977,7 +977,7 @@ class LibvirtConnection(object):
"""
Update compute manager resource info on Service table.
This method is called when nova-coompute launches, and
- whenever admin executes "nova-manage service updateresource".
+ whenever admin executes "nova-manage service update_resource".
"""
try:
--
cgit
From 205810c3da4652fd0f5203f53299cd998ac7cf82 Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: Fri, 18 Feb 2011 11:44:06 +0100
Subject: added functionality to list only fixed ip addresses of one node and
added exception handling to list method
# nova-manage fixed list XXXX
network IP address MAC address hostname host
10.xx.xx.0/24 10.xx.xx.5 02:16:3e:3f:33:b6 i-00000547 XXXX
10.xx.xx.0/24 10.xx.xx.9 02:16:3e:14:03:d6 i-00000548 XXXX
10.xx.xx.0/24 10.xx.xx.12 02:16:3e:20:1b:e7 i-00000549 XXXX
---
bin/nova-manage | 19 ++++++++++++-------
nova/db/api.py | 5 +++++
nova/db/sqlalchemy/api.py | 22 ++++++++++++++++++++++
3 files changed, 39 insertions(+), 7 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 6d67252b8..60a00f1cf 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -439,10 +439,15 @@ class FixedIpCommands(object):
def list(self, host=None):
"""Lists all fixed ips (optionally by host) arguments: [host]"""
ctxt = context.get_admin_context()
- if host == None:
- fixed_ips = db.fixed_ip_get_all(ctxt)
- else:
- fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)
+
+ try:
+ if host == None:
+ fixed_ips = db.fixed_ip_get_all(ctxt)
+ else:
+ fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)
+ except exception.NotFound as ex:
+ print "error: %s" % ex
+ sys.exit(2)
print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (_('network'),
_('IP address'),
@@ -459,9 +464,9 @@ class FixedIpCommands(object):
host = instance['host']
mac_address = instance['mac_address']
print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (
- fixed_ip['network']['cidr'],
- fixed_ip['address'],
- mac_address, hostname, host)
+ fixed_ip['network']['cidr'],
+ fixed_ip['address'],
+ mac_address, hostname, host)
class FloatingIpCommands(object):
diff --git a/nova/db/api.py b/nova/db/api.py
index d7f3746d2..6053c0352 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -293,6 +293,11 @@ def fixed_ip_get_all(context):
return IMPL.fixed_ip_get_all(context)
+def fixed_ip_get_all_by_host(context, host):
+ """Get all defined fixed ips used by a host."""
+ return IMPL.fixed_ip_get_all_by_host(context, host)
+
+
def fixed_ip_get_by_address(context, address):
"""Get a fixed ip by address or raise if it does not exist."""
return IMPL.fixed_ip_get_by_address(context, address)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 2697fac73..d07c53759 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -594,6 +594,28 @@ def fixed_ip_get_all(context, session=None):
return result
+@require_admin_context
+def fixed_ip_get_all_by_host(context, host=None):
+ session = get_session()
+
+ # FIXME: I'm sure that SQLAlchemy can handle this in a nicer way
+ instances = session.query(models.Instance).\
+ filter_by(state=1).\
+ filter_by(host=host).\
+ all()
+
+ result = []
+ for instance in instances:
+ result.append(session.query(models.FixedIp).\
+ filter_by(instance_id=instance['id']).\
+ first())
+
+ if not result:
+ raise exception.NotFound(_('No fixed ips for this host defined'))
+
+ return result
+
+
@require_context
def fixed_ip_get_by_address(context, address, session=None):
if not session:
--
cgit
From cf8cf8287169e3e0b996db7db5a135dea88db63a Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: Fri, 18 Feb 2011 12:01:50 +0100
Subject: added new class Instances to manage instances and added a new listing
method into the class
# nova-manage instance list
instance node type state launched image kernel ramdisk project user zone index
i-00000547 XXXXXXX m1.small running 2011-02-18 08:36:37 ami-a03ndz0q ami-0isqekvw testing berendt None 0
i-00000548 XXXXXXX m1.small running 2011-02-18 08:37:17 ami-a03ndz0q ami-0isqekvw testing berendt None 1
i-00000549 XXXXXXX m1.small running 2011-02-18 08:37:52 ami-a03ndz0q ami-0isqekvw testing berendt None 2
---
bin/nova-manage | 43 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 42 insertions(+), 1 deletion(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 60a00f1cf..68847bdde 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -618,6 +618,45 @@ class DbCommands(object):
print migration.db_version()
+class InstanceCommands(object):
+ """Class for managing instances."""
+
+ def list(self, host=None, instance=None):
+ """Show a list of all instances"""
+ print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s" \
+ " %-12s %-10s %-10s %-10s %-5s" % (
+ _('instance'),
+ _('node'),
+ _('type'),
+ _('state'),
+ _('launched'),
+ _('image'),
+ _('kernel'),
+ _('ramdisk'),
+ _('project'),
+ _('user'),
+ _('zone'),
+ _('index'),
+ )
+
+ for instance in db.instance_get_all(context.get_admin_context()):
+ print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
+ " %-10s %-10s %-10s %-5d" % (
+ instance['hostname'],
+ instance['host'],
+ instance['instance_type'],
+ instance['state_description'],
+ instance['launched_at'],
+ instance['image_id'],
+ instance['kernel_id'],
+ instance['ramdisk_id'],
+ instance['project_id'],
+ instance['user_id'],
+ instance['availability_zone'],
+ instance['launch_index']
+ )
+
+
class VolumeCommands(object):
"""Methods for dealing with a cloud in an odd state"""
@@ -677,7 +716,9 @@ CATEGORIES = [
('service', ServiceCommands),
('log', LogCommands),
('db', DbCommands),
- ('volume', VolumeCommands)]
+ ('volume', VolumeCommands)
+ ('instance', InstanceCommands),
+]
def lazy_match(name, key_value_tuples):
--
cgit
From 764f0a457e74c4498cbc9ea30a184e61f7932072 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Tue, 22 Feb 2011 13:18:21 +0900
Subject: just add 005_add_live_migration.py.
---
.../versions/005_add_live_migration.py | 84 ++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/005_add_live_migration.py
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/005_add_live_migration.py b/nova/db/sqlalchemy/migrate_repo/versions/005_add_live_migration.py
new file mode 100644
index 000000000..903f7a646
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/005_add_live_migration.py
@@ -0,0 +1,84 @@
+# 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.
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import log as logging
+
+
+meta = MetaData()
+
+instances = Table('instances', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+#
+# New Tables
+#
+
+compute_services = Table('compute_services', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('service_id', Integer(), nullable=False),
+
+ Column('vcpus', Integer(), nullable=False),
+ Column('memory_mb', Integer(), nullable=False),
+ Column('local_gb', Integer(), nullable=False),
+ Column('vcpus_used', Integer(), nullable=False),
+ Column('memory_mb_used', Integer(), nullable=False),
+ Column('local_gb_used', Integer(), nullable=False),
+ Column('hypervisor_type',
+ Text(convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False),
+ nullable=False),
+ Column('hypervisor_version', Integer(), nullable=False),
+ Column('cpu_info',
+ Text(convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False),
+ nullable=False),
+ )
+
+
+#
+# Tables to alter
+#
+instances_launched_on = Column(
+ 'launched_on',
+ Text(convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False),
+ nullable=True)
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ try:
+ compute_services.create()
+ except Exception:
+ logging.info(repr(compute_services))
+ logging.exception('Exception while creating table')
+ meta.drop_all(tables=[compute_services])
+ raise
+
+ instances.create_column(instances_launched_on)
--
cgit
From c32e57999be09368b18f5a89315465e629ed4819 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Tue, 22 Feb 2011 23:55:03 +0900
Subject: Fixed based on reviewer's comment. 1. Change docstrings format 2. Fix
comment grammer mistake, etc
---
bin/nova-api | 2 -
bin/nova-dhcpbridge | 1 -
bin/nova-manage | 28 +-
nova/compute/manager.py | 123 +++++---
nova/db/api.py | 12 +-
nova/db/sqlalchemy/api.py | 27 +-
.../versions/005_add_live_migration.py | 3 +-
nova/db/sqlalchemy/models.py | 12 +-
nova/scheduler/driver.py | 121 +++++---
nova/scheduler/manager.py | 15 +-
nova/tests/test_compute.py | 77 ++---
nova/tests/test_scheduler.py | 141 ++++------
nova/tests/test_service.py | 6 +-
nova/tests/test_virt.py | 309 +++++++++------------
nova/virt/fake.py | 74 +----
nova/virt/libvirt_conn.py | 182 ++++++++----
nova/virt/xenapi_conn.py | 14 +-
nova/volume/driver.py | 14 +-
18 files changed, 576 insertions(+), 585 deletions(-)
diff --git a/bin/nova-api b/bin/nova-api
index 59466a8c6..11176a021 100755
--- a/bin/nova-api
+++ b/bin/nova-api
@@ -38,13 +38,11 @@ from nova import flags
from nova import log as logging
from nova import version
from nova import wsgi
-from nova import utils
logging.basicConfig()
LOG = logging.getLogger('nova.api')
LOG.setLevel(logging.DEBUG)
-utils.default_flagfile()
FLAGS = flags.FLAGS
API_ENDPOINTS = ['ec2', 'osapi']
diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge
index fb04a484e..d38ba2543 100755
--- a/bin/nova-dhcpbridge
+++ b/bin/nova-dhcpbridge
@@ -125,7 +125,6 @@ def main():
LOG.debug(msg)
globals()[action + '_lease'](mac, ip, hostname, interface)
else:
- open('/tmp/aaa', 'w+').write('-- %s' % interface)
print init_leases(interface)
if __name__ == "__main__":
diff --git a/bin/nova-manage b/bin/nova-manage
index 696ce0cad..49246fcc8 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -548,7 +548,12 @@ class InstanceCommands(object):
"""Class for mangaging VM instances."""
def live_migration(self, ec2_id, dest):
- """Migrates a running instance to a new machine."""
+ """Migrates a running instance to a new machine.
+
+ :param ec2_id: instance id which comes from euca-describe-instance.
+ :param dest: destination host name.
+
+ """
ctxt = context.get_admin_context()
instance_id = ec2_id_to_id(ec2_id)
@@ -569,9 +574,8 @@ class InstanceCommands(object):
"dest": dest,
"topic": FLAGS.compute_topic}})
- msg = 'Migration of %s initiated. ' % ec2_id
- msg += 'Check its progress using euca-describe-instances.'
- print msg
+ print _('Migration of %s initiated.'
+ 'Check its progress using euca-describe-instances.') % ec2_id
class ServiceCommands(object):
@@ -619,15 +623,17 @@ class ServiceCommands(object):
db.service_update(ctxt, svc['id'], {'disabled': True})
def describe_resource(self, host):
- """describe cpu/memory/hdd info for host."""
+ """Describes cpu/memory/hdd info for host.
+
+ :param host: hostname.
+
+ """
result = rpc.call(context.get_admin_context(),
FLAGS.scheduler_topic,
- {"method": "show_host_resource",
+ {"method": "show_host_resources",
"args": {"host": host}})
- # Checking result msg format is necessary, that will have done
- # when this feture is included in API.
if type(result) != dict:
print 'Unexpected error occurs'
print '[Result]', result
@@ -650,7 +656,11 @@ class ServiceCommands(object):
val['local_gb'])
def update_resource(self, host):
- """update available vcpu/memory/disk info for host."""
+ """Updates available vcpu/memory/disk info for host.
+
+ :param host: hostname.
+
+ """
ctxt = context.get_admin_context()
service_refs = db.service_get_all_by_host(ctxt, host)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index d548cef6f..5b6e9082e 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -36,10 +36,10 @@ terminating it.
import base64
import datetime
+import os
import random
import string
import socket
-import os
import tempfile
import time
import functools
@@ -65,8 +65,8 @@ flags.DEFINE_string('console_host', socket.gethostname(),
'Console proxy host to use to connect to instances on'
'this host.')
flags.DEFINE_string('live_migration_retry_count', 30,
- ("""Retry count needed in live_migration."""
- """ sleep 1 sec for each count"""))
+ ("Retry count needed in live_migration."
+ " sleep 1 sec for each count"))
LOG = logging.getLogger('nova.compute.manager')
@@ -602,31 +602,66 @@ class ComputeManager(manager.Manager):
@exception.wrap_exception
def compare_cpu(self, context, cpu_info):
- """ Check the host cpu is compatible to a cpu given by xml."""
+ """Checks the host cpu is compatible to a cpu given by xml.
+
+ :param context: security context
+ :param cpu_info: json string obtained from virConnect.getCapabilities
+ :returns: See driver.compare_cpu
+
+ """
return self.driver.compare_cpu(cpu_info)
@exception.wrap_exception
def mktmpfile(self, context):
- """make tmpfile under FLAGS.instance_path."""
- fd, name = tempfile.mkstemp(dir=FLAGS.instances_path)
- # No essential reason to write dateinfo. just for debugging reason.
- os.fdopen(fd, 'w').write(str(datetime.datetime.utcnow()))
+ """Makes tmpfile under FLAGS.instance_path.
+
+ This method enables compute nodes to recognize that they mounts
+ same shared storage. mktmpfile()/confirm_tmpfile is a pair.
+
+ :param context: security context
+ :returns: tmpfile name
+
+ """
+
+ dirpath = FLAGS.instances_path
+ fd, name = tempfile.mkstemp(dir=dirpath)
+ LOG.debug(_("Creating tmpfile %s to notify to other "
+ "compute node that they mounts same storage.") % name)
+ os.fdopen(fd, 'w+').close()
return name
@exception.wrap_exception
def confirm_tmpfile(self, context, path):
- """Confirm existence of the tmpfile given by path."""
+ """Confirms existence of the tmpfile given by path.
+
+ :param context: security context
+ :param path: confirm existence of this path
+ :returns: depends on os.remove()
+
+ """
+
if not os.path.exists(path):
raise exception.NotFound(_('%s not found') % path)
return os.remove(path)
@exception.wrap_exception
def update_available_resource(self, context):
- """See comments update_resource_info"""
+ """See comments update_resource_info.
+
+ :param context: security context
+ :returns: See driver.update_available_resource()
+
+ """
+
return self.driver.update_available_resource(context, self.host)
def pre_live_migration(self, context, instance_id):
- """Any preparation for live migration at dst host."""
+ """Preparations for live migration at dest host.
+
+ :param context: security context
+ :param instance_id: nova.db.sqlalchemy.models.Instance.Id
+
+ """
# Getting instance info
instance_ref = self.db.instance_get(context, instance_id)
@@ -635,7 +670,7 @@ class ComputeManager(manager.Manager):
# Getting fixed ips
fixed_ip = self.db.instance_get_fixed_address(context, instance_id)
if not fixed_ip:
- msg = _("%(instance_id)s(%(ec2_id)s) doesnt have fixed_ip")
+ msg = _("%(instance_id)s(%(ec2_id)s) does'nt have fixed_ip")
raise exception.NotFound(msg % locals())
# If any volume is mounted, prepare here.
@@ -645,8 +680,8 @@ class ComputeManager(manager.Manager):
for v in instance_ref['volumes']:
self.volume_manager.setup_compute_volume(context, v['id'])
- # Bridge settings
- # call this method prior to ensure_filtering_rules_for_instance,
+ # Bridge settings.
+ # Call this method prior to ensure_filtering_rules_for_instance,
# since bridge is not set up, ensure_filtering_rules_for instance
# fails.
#
@@ -660,24 +695,29 @@ class ComputeManager(manager.Manager):
break
except exception.ProcessExecutionError, e:
if i == max_retry - 1:
- raise e
+ raise
else:
- LOG.warn(_("setup_compute_network() fail %(i)d th. "
- "Retry up to %(max_retry)d for %(ec2_id)s")
+ LOG.warn(_("setup_compute_network() failed %(i)d."
+ "Retry up to %(max_retry)d for %(ec2_id)s.")
% locals())
time.sleep(1)
# Creating filters to hypervisors and firewalls.
# An example is that nova-instance-instance-xxx,
- # which is written to libvirt.xml( check "virsh nwfilter-list )
- # On destination host, this nwfilter is necessary.
+ # which is written to libvirt.xml(Check "virsh nwfilter-list")
+ # This nwfilter is necessary on the destination host.
# In addition, this method is creating filtering rule
# onto destination host.
self.driver.ensure_filtering_rules_for_instance(instance_ref)
- #@exception.wrap_exception
def live_migration(self, context, instance_id, dest):
- """Executing live migration."""
+ """Executing live migration.
+
+ :param context: security context
+ :param instance_id: nova.db.sqlalchemy.models.Instance.Id
+ :param dest: destination host
+
+ """
# Get instance for error handling.
instance_ref = self.db.instance_get(context, instance_id)
@@ -702,7 +742,7 @@ class ComputeManager(manager.Manager):
msg = _("Pre live migration for %(i_name)s failed at %(dest)s")
LOG.error(msg % locals())
self.recover_live_migration(context, instance_ref)
- raise e
+ raise
# Executing live migration
# live_migration might raises exceptions, but
@@ -712,10 +752,17 @@ class ComputeManager(manager.Manager):
self.recover_live_migration)
def post_live_migration(self, ctxt, instance_ref, dest):
+ """Post operations for live migration.
+
+ This method is called from live_migration
+ and mainly updating database record.
+
+ :param ctxt: security context
+ :param instance_id: nova.db.sqlalchemy.models.Instance.Id
+ :param dest: destination host
+
"""
- Post operations for live migration.
- Mainly, database updating.
- """
+
LOG.info(_('post_live_migration() is started..'))
instance_id = instance_ref['id']
@@ -734,19 +781,12 @@ class ComputeManager(manager.Manager):
# Database updating.
i_name = instance_ref.name
- #fixed_ip = self.db.instance_get_fixed_address(ctxt, instance_id)
- # Not return if fixed_ip is not found, otherwise,
- # instance never be accessible..
- #if None == fixed_ip:
- # LOG.warn(_('fixed_ip is not found for %s.') % i_name)
- #self.db.fixed_ip_update(ctxt, fixed_ip, {'host': dest})
-
try:
# Not return if floating_ip is not found, otherwise,
# instance never be accessible..
floating_ip = self.db.instance_get_floating_address(ctxt,
instance_id)
- if None == floating_ip:
+ if not floating_ip:
LOG.info(_('floating_ip is not found for %s'), i_name)
else:
floating_ip_ref = self.db.floating_ip_get_by_address(ctxt,
@@ -763,15 +803,23 @@ class ComputeManager(manager.Manager):
# Restore instance/volume state
self.recover_live_migration(ctxt, instance_ref, dest)
- msg = _('Migrating %(i_name)s to %(dest)s finishes successfully.')
- LOG.info(msg % locals())
+ LOG.info(_('Migrating %(i_name)s to %(dest)s finishes successfully.')
+ % locals())
LOG.info(_("The below error is normally occurs."
"Just check if instance is successfully migrated.\n"
"libvir: QEMU error : Domain not found: no domain "
"with matching name.."))
def recover_live_migration(self, ctxt, instance_ref, host=None):
- """Instance/volume state is recovered from migrating -> running."""
+ """Recovers Instance/volume state from migrating -> running.
+
+ :param ctxt: security context
+ :param instance_id: nova.db.sqlalchemy.models.Instance.Id
+ :param host:
+ DB column value is updated by this hostname.
+ if none, the host instance currently running is selected.
+
+ """
if not host:
host = instance_ref['host']
@@ -783,5 +831,4 @@ class ComputeManager(manager.Manager):
'host': host})
for v in instance_ref['volumes']:
- self.db.volume_update(ctxt, v['id'], {'status': 'in-use',
- 'host': host})
+ self.db.volume_update(ctxt, v['id'], {'status': 'in-use'})
diff --git a/nova/db/api.py b/nova/db/api.py
index 609f62495..e10a06178 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -169,6 +169,7 @@ def compute_service_update(context, compute_id, values):
Raises NotFound if computeService does not exist.
"""
+
return IMPL.compute_service_update(context, compute_id, values)
@@ -446,27 +447,22 @@ def instance_add_security_group(context, instance_id, security_group_id):
security_group_id)
-def instance_get_all_by_host(context, hostname):
- """Get instances by host"""
- return IMPL.instance_get_all_by_host(context, hostname)
-
-
def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id):
- """Get instances.vcpus by host and project"""
+ """Get instances.vcpus by host and project."""
return IMPL.instance_get_vcpu_sum_by_host_and_project(context,
hostname,
proj_id)
def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id):
- """Get amount of memory by host and project """
+ """Get amount of memory by host and project."""
return IMPL.instance_get_memory_sum_by_host_and_project(context,
hostname,
proj_id)
def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id):
- """Get total amount of disk by host and project """
+ """Get total amount of disk by host and project."""
return IMPL.instance_get_disk_sum_by_host_and_project(context,
hostname,
proj_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 43d56cd8a..b4f45a089 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -184,8 +184,8 @@ def service_get_all_compute_by_host(context, host):
all()
if not result:
- msg = _('%s does not exist or not compute node')
- raise exception.NotFound(msg % host)
+ raise exception.NotFound(_("%s does not exist or not "
+ "compute node.") % host)
return result
@@ -328,7 +328,7 @@ def compute_service_create(context, values):
def compute_service_update(context, compute_id, values):
session = get_session()
with session.begin():
- compute_ref = service_get(context, compute_id, session=session)
+ compute_ref = compute_service_get(context, compute_id, session=session)
compute_ref.update(values)
compute_ref.save(session=session)
@@ -964,21 +964,6 @@ def instance_add_security_group(context, instance_id, security_group_id):
instance_ref.save(session=session)
-@require_context
-def instance_get_all_by_host(context, hostname):
- session = get_session()
- if not session:
- session = get_session()
-
- result = session.query(models.Instance).\
- filter_by(host=hostname).\
- filter_by(deleted=can_read_deleted(context)).\
- all()
- if not result:
- return []
- return result
-
-
@require_context
def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id):
session = get_session()
@@ -987,7 +972,7 @@ def instance_get_vcpu_sum_by_host_and_project(context, hostname, proj_id):
filter_by(project_id=proj_id).\
filter_by(deleted=False).\
value(func.sum(models.Instance.vcpus))
- if None == result:
+ if not result:
return 0
return result
@@ -1000,7 +985,7 @@ def instance_get_memory_sum_by_host_and_project(context, hostname, proj_id):
filter_by(project_id=proj_id).\
filter_by(deleted=False).\
value(func.sum(models.Instance.memory_mb))
- if None == result:
+ if not result:
return 0
return result
@@ -1013,7 +998,7 @@ def instance_get_disk_sum_by_host_and_project(context, hostname, proj_id):
filter_by(project_id=proj_id).\
filter_by(deleted=False).\
value(func.sum(models.Instance.local_gb))
- if None == result:
+ if not result:
return 0
return result
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/005_add_live_migration.py b/nova/db/sqlalchemy/migrate_repo/versions/005_add_live_migration.py
index 903f7a646..2689b5b74 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/005_add_live_migration.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/005_add_live_migration.py
@@ -16,10 +16,9 @@
# 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 *
meta = MetaData()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 02d4e2f9b..f2a029c20 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -137,12 +137,14 @@ class ComputeService(BASE, NovaBase):
# Note(masumotok): Expected Strings example:
#
- # '{"arch":"x86_64", "model":"Nehalem",
- # "topology":{"sockets":1, "threads":2, "cores":3},
- # features:[ "tdtscp", "xtpr"]}'
+ # '{"arch":"x86_64",
+ # "model":"Nehalem",
+ # "topology":{"sockets":1, "threads":2, "cores":3},
+ # "features":["tdtscp", "xtpr"]}'
#
# Points are "json translatable" and it must have all dictionary keys
- # above, and tag of getCapabilities()(See libvirt.virtConnection).
+ # above, since it is copied from tag of getCapabilities()
+ # (See libvirt.virtConnection).
cpu_info = Column(Text, nullable=True)
@@ -220,7 +222,7 @@ class Instance(BASE, NovaBase):
display_description = Column(String(255))
# To remember on which host a instance booted.
- # An instance may moved to other host by live migraiton.
+ # An instance may have moved to another host by live migraiton.
launched_on = Column(Text)
locked = Column(Boolean)
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 41b87bbca..8c30702ba 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -70,9 +70,18 @@ class Scheduler(object):
raise NotImplementedError(_("Must implement a fallback schedule"))
def schedule_live_migration(self, context, instance_id, dest):
- """live migration method"""
+ """Live migration scheduling method.
- # Whether instance exists and running
+ :param context:
+ :param instance_id:
+ :param dest: destination host
+ :return:
+ The host where instance is running currently.
+ Then scheduler send request that host.
+
+ """
+
+ # Whether instance exists and is running.
instance_ref = db.instance_get(context, instance_id)
# Checking instance.
@@ -102,11 +111,16 @@ class Scheduler(object):
return src
def _live_migration_src_check(self, context, instance_ref):
- """Live migration check routine (for src host)"""
+ """Live migration check routine (for src host).
+
+ :param context: security context
+ :param instance_ref: nova.db.sqlalchemy.models.Instance object
+
+ """
# Checking instance is running.
- if power_state.RUNNING != instance_ref['state'] or \
- 'running' != instance_ref['state_description']:
+ if (power_state.RUNNING != instance_ref['state'] or \
+ 'running' != instance_ref['state_description']):
msg = _('Instance(%s) is not running')
ec2_id = instance_ref['hostname']
raise exception.Invalid(msg % ec2_id)
@@ -129,7 +143,13 @@ class Scheduler(object):
raise exception.Invalid(msg % src)
def _live_migration_dest_check(self, context, instance_ref, dest):
- """Live migration check routine (for destination host)"""
+ """Live migration check routine (for destination host).
+
+ :param context: security context
+ :param instance_ref: nova.db.sqlalchemy.models.Instance object
+ :param dest: destination host
+
+ """
# Checking dest exists and compute node.
dservice_refs = db.service_get_all_compute_by_host(context, dest)
@@ -145,20 +165,25 @@ class Scheduler(object):
src = instance_ref['host']
if dest == src:
ec2_id = instance_ref['hostname']
- msg = _("""%(dest)s is where %(ec2_id)s is """
- """running now. choose other host.""") % locals()
- raise exception.Invalid(msg)
+ raise exception.Invalid(_("%(dest)s is where %(ec2_id)s is "
+ "running now. choose other host.")
+ % locals())
# Checking dst host still has enough capacities.
- self.has_enough_resource(context, instance_ref, dest)
+ self.has_enough_resources(context, instance_ref, dest)
def _live_migration_common_check(self, context, instance_ref, dest):
- """
- Live migration check routine.
- Below pre-checkings are followed by
+ """Live migration common check routine.
+
+ Below checkings are followed by
http://wiki.libvirt.org/page/TodoPreMigrationChecks
+ :param context: security context
+ :param instance_ref: nova.db.sqlalchemy.models.Instance object
+ :param dest: destination host
+
"""
+
# Checking shared storage connectivity
self.mounted_on_same_shared_storage(context, instance_ref, dest)
@@ -168,27 +193,27 @@ class Scheduler(object):
# Checking original host( where instance was launched at) exists.
try:
- oservice_refs = \
- db.service_get_all_compute_by_host(context,
- instance_ref['launched_on'])
+ oservice_refs = db.service_get_all_compute_by_host(context,
+ instance_ref['launched_on'])
except exception.NotFound:
- msg = _('%s(where instance was launched at) does not exists.')
- raise exception.Invalid(msg % instance_ref['launched_on'])
+ raise exception.Invalid(_("host %s where instance was launched "
+ "does not exist.")
+ % instance_ref['launched_on'])
oservice_ref = oservice_refs[0]['compute_service'][0]
# Checking hypervisor is same.
o = oservice_ref['hypervisor_type']
d = dservice_ref['hypervisor_type']
if o != d:
- msg = _('Different hypervisor type(%(o)s->%(d)s)') % locals()
- raise exception.Invalid(msg)
+ raise exception.Invalid(_("Different hypervisor type"
+ "(%(o)s->%(d)s)')" % locals()))
# Checkng hypervisor version.
o = oservice_ref['hypervisor_version']
d = dservice_ref['hypervisor_version']
if o > d:
- msg = _('Older hypervisor version(%(o)s->%(d)s)') % locals()
- raise exception.Invalid(msg)
+ raise exception.Invalid(_('Older hypervisor version(%(o)s->%(d)s)')
+ % locals())
# Checking cpuinfo.
try:
@@ -200,19 +225,24 @@ class Scheduler(object):
except rpc.RemoteError, e:
ec2_id = instance_ref['hostname']
src = instance_ref['host']
- msg = _("""%(dest)s doesnt have compatibility to %(src)s"""
- """(where %(ec2_id)s was launched at)""")
- logging.exception(msg % locals())
- raise e
+ logging.exception(_("host %(dest)s is not compatible with "
+ "original host %(src)s.") % locals())
+ raise
+
+ def has_enough_resources(self, context, instance_ref, dest):
+ """Checks if destination host has enough resource for live migration.
- def has_enough_resource(self, context, instance_ref, dest):
- """
- Check if destination host has enough resource for live migration.
Currently, only memory checking has been done.
If storage migration(block migration, meaning live-migration
without any shared storage) will be available, local storage
checking is also necessary.
+
+ :param context: security context
+ :param instance_ref: nova.db.sqlalchemy.models.Instance object
+ :param dest: destination host
+
"""
+
# Getting instance information
ec2_id = instance_ref['hostname']
@@ -225,15 +255,23 @@ class Scheduler(object):
mem_avail = mem_total - mem_used
mem_inst = instance_ref['memory_mb']
if mem_avail <= mem_inst:
- msg = _("""%(ec2_id)s is not capable to migrate %(dest)s"""
- """(host:%(mem_avail)s <= instance:%(mem_inst)s)""")
- raise exception.NotEmpty(msg % locals())
+ raise exception.NotEmpty(_("%(ec2_id)s is not capable to "
+ "migrate %(dest)s (host:%(mem_avail)s "
+ " <= instance:%(mem_inst)s)")
+ % locals())
def mounted_on_same_shared_storage(self, context, instance_ref, dest):
+ """Check if the src and dest host mount same shared storage.
+
+ At first, dest host creates temp file, and src host can see
+ it if they mounts same shared storage. Then src host erase it.
+
+ :param context: security context
+ :param instance_ref: nova.db.sqlalchemy.models.Instance object
+ :param dest: destination host
+
"""
- Check if /nova-inst-dir/insntances is mounted same storage at
- live-migration src and dest host.
- """
+
src = instance_ref['host']
dst_t = db.queue_get_for(context, FLAGS.compute_topic, dest)
src_t = db.queue_get_for(context, FLAGS.compute_topic, src)
@@ -243,8 +281,8 @@ class Scheduler(object):
filename = rpc.call(context, dst_t, {"method": 'mktmpfile'})
except rpc.RemoteError, e:
msg = _("Cannot create tmpfile at %s to confirm shared storage.")
- logging.error(msg % FLAGS.instance_path)
- raise e
+ LOG.error(msg % FLAGS.instances_path)
+ raise
# make sure existence at src host.
try:
@@ -252,8 +290,7 @@ class Scheduler(object):
{"method": 'confirm_tmpfile', "args": {'path': filename}})
except (rpc.RemoteError, exception.NotFound), e:
- ipath = FLAGS.instance_path
- msg = _("""Cannot comfirm %(ipath)s at %(dest)s is located at"""
- """ same shared storage.""") % locals()
- logging.error(msg)
- raise e
+ ipath = FLAGS.instances_path
+ logging.error(_("Cannot comfirm %(ipath)s at %(dest)s is "
+ "located at same shared storage.") % locals())
+ raise
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index 584dc49d2..783594c6f 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -69,10 +69,19 @@ class SchedulerManager(manager.Manager):
LOG.debug(_("Casting to %(topic)s %(host)s for %(method)s") % locals())
# NOTE (masumotok) : This method should be moved to nova.api.ec2.admin.
- # Based on bear design summit discussion,
+ # Based on bexar design summit discussion,
# just put this here for bexar release.
- def show_host_resource(self, context, host, *args):
- """show the physical/usage resource given by hosts."""
+ def show_host_resources(self, context, host, *args):
+ """Shows the physical/usage resource given by hosts.
+
+ :param context: security context
+ :param host: hostname
+ :returns:
+ example format is below.
+ {'resource':D, 'usage':{proj_id1:D, proj_id2:D}}
+ D: {'vcpus':3, 'memory_mb':2048, 'local_gb':2048}
+
+ """
compute_ref = db.service_get_all_compute_by_host(context, host)
compute_ref = compute_ref[0]
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 74cb82eeb..3c88d186d 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -314,10 +314,7 @@ class ComputeTestCase(test.TestCase):
self.compute_driver = utils.import_object(FLAGS.compute_driver)
def test_pre_live_migration_instance_has_no_fixed_ip(self):
- """
- if instances that are intended to be migrated doesnt have fixed_ip
- (not happens usually), pre_live_migration has to raise Exception.
- """
+ """Confirm raising exception if instance doesn't have fixed_ip."""
instance_ref = self._get_dummy_instance()
c = context.get_admin_context()
i_id = instance_ref['id']
@@ -331,14 +328,9 @@ class ComputeTestCase(test.TestCase):
self.assertRaises(exception.NotFound,
self.compute.pre_live_migration,
c, instance_ref['id'])
- self.mox.ResetAll()
def test_pre_live_migration_instance_has_volume(self):
- """if any volumes are attached to the instances that are
- intended to be migrated, setup_compute_volume must be
- called because aoe module should be inserted at destination
- host. This testcase checks on it.
- """
+ """Confirm setup_compute_volume is called when volume is mounted."""
i_ref = self._get_dummy_instance()
c = context.get_admin_context()
@@ -364,14 +356,9 @@ class ComputeTestCase(test.TestCase):
self.mox.ReplayAll()
ret = self.compute.pre_live_migration(c, i_ref['id'])
self.assertEqual(ret, None)
- self.mox.ResetAll()
def test_pre_live_migration_instance_has_no_volume(self):
- """if any volumes are not attached to the instances that are
- intended to be migrated, log message should be appears
- because administrator can proove instance conditions before
- live_migration if any trouble occurs.
- """
+ """Confirm log meg when instance doesn't mount any volumes."""
i_ref = self._get_dummy_instance()
i_ref.__setitem__('volumes', [])
c = context.get_admin_context()
@@ -395,14 +382,14 @@ class ComputeTestCase(test.TestCase):
self.mox.ReplayAll()
ret = self.compute.pre_live_migration(c, i_ref['id'])
self.assertEqual(ret, None)
- self.mox.ResetAll()
def test_pre_live_migration_setup_compute_node_fail(self):
- """setup_compute_node sometimes fail since concurrent request
- comes to iptables and iptables complains. Then this method
- tries to retry, but raise exception in case of over
- max_retry_count. this method confirms raising exception.
+ """Confirm operation setup_compute_network() fails.
+
+ It retries and raise exception when timeout exceeded.
+
"""
+
i_ref = self._get_dummy_instance()
c = context.get_admin_context()
@@ -427,14 +414,9 @@ class ComputeTestCase(test.TestCase):
self.assertRaises(exception.ProcessExecutionError,
self.compute.pre_live_migration,
c, i_ref['id'])
- self.mox.ResetAll()
- def test_live_migration_instance_has_volume(self):
- """Any volumes are mounted by instances to be migrated are found,
- vblade health must be checked before starting live-migration.
- And that is checked by check_for_export().
- This testcase confirms check_for_export() is called.
- """
+ def test_live_migration_works_correctly_with_volume(self):
+ """Confirm check_for_export to confirm volume health check."""
i_ref = self._get_dummy_instance()
c = context.get_admin_context()
topic = db.queue_get_for(c, FLAGS.compute_topic, i_ref['host'])
@@ -457,15 +439,9 @@ class ComputeTestCase(test.TestCase):
self.mox.ReplayAll()
ret = self.compute.live_migration(c, i_ref['id'], i_ref['host'])
self.assertEqual(ret, None)
- self.mox.ResetAll()
-
- def test_live_migration_instance_has_volume_and_exception(self):
- """In addition to test_live_migration_instance_has_volume testcase,
- this testcase confirms if any exception raises from
- check_for_export(). Then, valid seaquence of this method should
- recovering instance/volumes status(ex. instance['state_description']
- is changed from 'migrating' -> 'running', was changed by scheduler)
- """
+
+ def test_live_migration_dest_raises_exception(self):
+ """Confirm exception when pre_live_migration fails."""
i_ref = self._get_dummy_instance()
c = context.get_admin_context()
topic = db.queue_get_for(c, FLAGS.compute_topic, i_ref['host'])
@@ -484,20 +460,16 @@ class ComputeTestCase(test.TestCase):
'state': power_state.RUNNING,
'host': i_ref['host']})
for v in i_ref['volumes']:
- dbmock.volume_update(c, v['id'], {'status': 'in-use',
- 'host': i_ref['host']})
+ dbmock.volume_update(c, v['id'], {'status': 'in-use'})
self.compute.db = dbmock
self.mox.ReplayAll()
self.assertRaises(rpc.RemoteError,
self.compute.live_migration,
c, i_ref['id'], i_ref['host'])
- self.mox.ResetAll()
- def test_live_migration_instance_has_no_volume_and_exception(self):
- """Simpler than
- test_live_migration_instance_has_volume_and_exception
- """
+ def test_live_migration_dest_raises_exception_no_volume(self):
+ """Same as above test(input pattern is different) """
i_ref = self._get_dummy_instance()
i_ref.__setitem__('volumes', [])
c = context.get_admin_context()
@@ -520,10 +492,9 @@ class ComputeTestCase(test.TestCase):
self.assertRaises(rpc.RemoteError,
self.compute.live_migration,
c, i_ref['id'], i_ref['host'])
- self.mox.ResetAll()
- def test_live_migration_instance_has_no_volume(self):
- """Simpler than test_live_migration_instance_has_volume."""
+ def test_live_migration_works_correctly_no_volume(self):
+ """Confirm live_migration() works as expected correctly."""
i_ref = self._get_dummy_instance()
i_ref.__setitem__('volumes', [])
c = context.get_admin_context()
@@ -545,11 +516,9 @@ class ComputeTestCase(test.TestCase):
self.mox.ReplayAll()
ret = self.compute.live_migration(c, i_ref['id'], i_ref['host'])
self.assertEqual(ret, None)
- self.mox.ResetAll()
def test_post_live_migration_working_correctly(self):
- """post_live_migration works as expected correctly """
-
+ """Confirm post_live_migration() works as expected correctly."""
dest = 'desthost'
flo_addr = '1.2.1.2'
@@ -579,19 +548,15 @@ class ComputeTestCase(test.TestCase):
# executing
self.mox.ReplayAll()
ret = self.compute.post_live_migration(c, i_ref, dest)
- self.mox.UnsetStubs()
# make sure every data is rewritten to dest
i_ref = db.instance_get(c, i_ref['id'])
c1 = (i_ref['host'] == dest)
- v_ref = db.volume_get(c, v_ref['id'])
- c2 = (v_ref['host'] == dest)
- c3 = False
flo_refs = db.floating_ip_get_all_by_host(c, dest)
- c3 = (len(flo_refs) != 0 and flo_refs[0]['address'] == flo_addr)
+ c2 = (len(flo_refs) != 0 and flo_refs[0]['address'] == flo_addr)
# post operaton
- self.assertTrue(c1 and c2 and c3)
+ self.assertTrue(c1 and c2)
db.instance_destroy(c, instance_id)
db.volume_destroy(c, v_ref['id'])
db.floating_ip_destroy(c, flo_addr)
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index 729bcb580..301106848 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -108,22 +108,21 @@ class SchedulerTestCase(test.TestCase):
self.mox.ReplayAll()
scheduler.named_method(ctxt, 'topic', num=7)
- def test_show_host_resource_host_not_exit(self):
- """
- A testcase of driver.has_enough_resource
- given host does not exists.
- """
+ def test_show_host_resources_host_not_exit(self):
+ """A host given as an argument does not exists."""
+
scheduler = manager.SchedulerManager()
dest = 'dummydest'
ctxt = context.get_admin_context()
try:
- scheduler.show_host_resource(ctxt, dest)
+ scheduler.show_host_resources(ctxt, dest)
except exception.NotFound, e:
c1 = (0 <= e.message.find('does not exist or not compute node'))
self.assertTrue(c1)
def _dic_is_equal(self, dic1, dic2, keys=None):
+ """Compares 2 dictionary contents(Helper method)"""
if not keys:
keys = ['vcpus', 'memory_mb', 'local_gb',
'vcpus_used', 'memory_mb_used', 'local_gb_used']
@@ -133,16 +132,14 @@ class SchedulerTestCase(test.TestCase):
return False
return True
- def test_show_host_resource_no_project(self):
- """
- A testcase of driver.show_host_resource
- no instance stays on the given host
- """
+ def test_show_host_resources_no_project(self):
+ """No instance are running on the given host."""
+
scheduler = manager.SchedulerManager()
ctxt = context.get_admin_context()
s_ref = self._create_compute_service()
- result = scheduler.show_host_resource(ctxt, s_ref['host'])
+ result = scheduler.show_host_resources(ctxt, s_ref['host'])
# result checking
c1 = ('resource' in result and 'usage' in result)
@@ -152,11 +149,9 @@ class SchedulerTestCase(test.TestCase):
self.assertTrue(c1 and c2 and c3)
db.service_destroy(ctxt, s_ref['id'])
- def test_show_host_resource_works_correctly(self):
- """
- A testcase of driver.show_host_resource
- to make sure everything finished with no error.
- """
+ def test_show_host_resources_works_correctly(self):
+ """show_host_resources() works correctly as expected."""
+
scheduler = manager.SchedulerManager()
ctxt = context.get_admin_context()
s_ref = self._create_compute_service()
@@ -164,7 +159,7 @@ class SchedulerTestCase(test.TestCase):
i_ref2 = self._create_instance(project_id='p-02', vcpus=3,
host=s_ref['host'])
- result = scheduler.show_host_resource(ctxt, s_ref['host'])
+ result = scheduler.show_host_resources(ctxt, s_ref['host'])
c1 = ('resource' in result and 'usage' in result)
compute_service = s_ref['compute_service'][0]
@@ -284,6 +279,7 @@ class SimpleDriverTestCase(test.TestCase):
return db.volume_create(self.context, vol)['id']
def _create_compute_service(self, **kwargs):
+ """Create a compute service."""
dic = {'binary': 'nova-compute', 'topic': 'compute',
'report_count': 0, 'availability_zone': 'dummyzone'}
@@ -698,13 +694,13 @@ class SimpleDriverTestCase(test.TestCase):
volume1.kill()
volume2.kill()
- def test_scheduler_live_migraiton_with_volume(self):
- """
- driver.scheduler_live_migration finishes successfully
- (volumes are attached to instances)
- This testcase make sure schedule_live_migration
- changes instance state from 'running' -> 'migrating'
+ def test_scheduler_live_migration_with_volume(self):
+ """scheduler_live_migration() works correctly as expected.
+
+ Also, checks instance state is changed from 'running' -> 'migrating'.
+
"""
+
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
dic = {'instance_id': instance_id, 'size': 1}
@@ -737,11 +733,9 @@ class SimpleDriverTestCase(test.TestCase):
db.instance_destroy(self.context, instance_id)
db.volume_destroy(self.context, v_ref['id'])
- def test_live_migraiton_src_check_instance_not_running(self):
- """
- A testcase of driver._live_migration_src_check.
- The instance given by instance_id is not running.
- """
+ def test_live_migration_src_check_instance_not_running(self):
+ """The instance given by instance_id is not running."""
+
instance_id = self._create_instance(state_description='migrating')
i_ref = db.instance_get(self.context, instance_id)
@@ -754,12 +748,9 @@ class SimpleDriverTestCase(test.TestCase):
self.assertTrue(c)
db.instance_destroy(self.context, instance_id)
- def test_live_migraiton_src_check_volume_node_not_alive(self):
- """
- A testcase of driver._live_migration_src_check.
- Volume node is not alive if any volumes are attached to
- the given instance.
- """
+ def test_live_migration_src_check_volume_node_not_alive(self):
+ """Raise exception when volume node is not alive."""
+
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
dic = {'instance_id': instance_id, 'size': 1}
@@ -782,11 +773,8 @@ class SimpleDriverTestCase(test.TestCase):
db.service_destroy(self.context, s_ref['id'])
db.volume_destroy(self.context, v_ref['id'])
- def test_live_migraiton_src_check_compute_node_not_alive(self):
- """
- A testcase of driver._live_migration_src_check.
- The testcase make sure src-compute node is alive.
- """
+ def test_live_migration_src_check_compute_node_not_alive(self):
+ """Confirms src-compute node is alive."""
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
t = datetime.datetime.utcnow() - datetime.timedelta(10)
@@ -803,11 +791,8 @@ class SimpleDriverTestCase(test.TestCase):
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
- def test_live_migraiton_src_check_works_correctly(self):
- """
- A testcase of driver._live_migration_src_check.
- The testcase make sure everything finished with no error.
- """
+ def test_live_migration_src_check_works_correctly(self):
+ """Confirms this method finishes with no error."""
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
s_ref = self._create_compute_service(host=i_ref['host'])
@@ -819,11 +804,8 @@ class SimpleDriverTestCase(test.TestCase):
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
- def test_live_migraiton_dest_check_not_alive(self):
- """
- A testcase of driver._live_migration_dst_check.
- Destination host does not exist.
- """
+ def test_live_migration_dest_check_not_alive(self):
+ """Confirms exception raises in case dest host does not exist."""
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
t = datetime.datetime.utcnow() - datetime.timedelta(10)
@@ -841,11 +823,8 @@ class SimpleDriverTestCase(test.TestCase):
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
- def test_live_migraiton_dest_check_service_same_host(self):
- """
- A testcase of driver._live_migration_dst_check.
- Destination host is same as src host.
- """
+ def test_live_migration_dest_check_service_same_host(self):
+ """Confirms exceptioin raises in case dest and src is same host."""
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
s_ref = self._create_compute_service(host=i_ref['host'])
@@ -861,11 +840,8 @@ class SimpleDriverTestCase(test.TestCase):
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
- def test_live_migraiton_dest_check_service_lack_memory(self):
- """
- A testcase of driver._live_migration_dst_check.
- destination host doesnt have enough memory.
- """
+ def test_live_migration_dest_check_service_lack_memory(self):
+ """Confirms exception raises when dest doesn't have enough memory."""
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
s_ref = self._create_compute_service(host='somewhere',
@@ -882,11 +858,8 @@ class SimpleDriverTestCase(test.TestCase):
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
- def test_live_migraiton_dest_check_service_works_correctly(self):
- """
- A testcase of driver._live_migration_dst_check.
- The testcase make sure everything finished with no error.
- """
+ def test_live_migration_dest_check_service_works_correctly(self):
+ """Confirms method finishes with no error."""
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
s_ref = self._create_compute_service(host='somewhere',
@@ -899,13 +872,11 @@ class SimpleDriverTestCase(test.TestCase):
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
- def test_live_migraiton_common_check_service_orig_not_exists(self):
- """
- A testcase of driver._live_migration_common_check.
- Destination host does not exist.
- """
+ def test_live_migration_common_check_service_orig_not_exists(self):
+ """Destination host does not exist."""
+
dest = 'dummydest'
- # mocks for live_migraiton_common_check()
+ # mocks for live_migration_common_check()
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
t1 = datetime.datetime.utcnow() - datetime.timedelta(10)
@@ -929,18 +900,15 @@ class SimpleDriverTestCase(test.TestCase):
i_ref,
dest)
except exception.Invalid, e:
- c = (e.message.find('does not exists') >= 0)
+ c = (e.message.find('does not exist') >= 0)
self.assertTrue(c)
self.mox.UnsetStubs()
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
- def test_live_migraiton_common_check_service_different_hypervisor(self):
- """
- A testcase of driver._live_migration_common_check.
- Original host and dest host has different hypervisor type.
- """
+ def test_live_migration_common_check_service_different_hypervisor(self):
+ """Original host and dest host has different hypervisor type."""
dest = 'dummydest'
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
@@ -969,11 +937,8 @@ class SimpleDriverTestCase(test.TestCase):
db.service_destroy(self.context, s_ref['id'])
db.service_destroy(self.context, s_ref2['id'])
- def test_live_migraiton_common_check_service_different_version(self):
- """
- A testcase of driver._live_migration_common_check.
- Original host and dest host has different hypervisor version.
- """
+ def test_live_migration_common_check_service_different_version(self):
+ """Original host and dest host has different hypervisor version."""
dest = 'dummydest'
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
@@ -1003,11 +968,9 @@ class SimpleDriverTestCase(test.TestCase):
db.service_destroy(self.context, s_ref['id'])
db.service_destroy(self.context, s_ref2['id'])
- def test_live_migraiton_common_check_service_checking_cpuinfo_fail(self):
- """
- A testcase of driver._live_migration_common_check.
- Original host and dest host has different hypervisor version.
- """
+ def test_live_migration_common_check_checking_cpuinfo_fail(self):
+ """Raise excetion when original host doen't have compatible cpu."""
+
dest = 'dummydest'
instance_id = self._create_instance()
i_ref = db.instance_get(self.context, instance_id)
@@ -1025,7 +988,7 @@ class SimpleDriverTestCase(test.TestCase):
rpc.call(mox.IgnoreArg(), mox.IgnoreArg(),
{"method": 'compare_cpu',
"args": {'cpu_info': s_ref2['compute_service'][0]['cpu_info']}}).\
- AndRaise(rpc.RemoteError('doesnt have compatibility to', '', ''))
+ AndRaise(rpc.RemoteError("doesn't have compatibility to", "", ""))
self.mox.ReplayAll()
try:
@@ -1033,7 +996,7 @@ class SimpleDriverTestCase(test.TestCase):
i_ref,
dest)
except rpc.RemoteError, e:
- c = (e.message.find(_('doesnt have compatibility to')) >= 0)
+ c = (e.message.find(_("doesn't have compatibility to")) >= 0)
self.assertTrue(c)
self.mox.UnsetStubs()
diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py
index cb65584cf..bbd5c6d92 100644
--- a/nova/tests/test_service.py
+++ b/nova/tests/test_service.py
@@ -279,11 +279,7 @@ class ServiceTestCase(test.TestCase):
self.assert_(not serv.model_disconnected)
def test_compute_can_update_available_resource(self):
- """
- Test nova-compute successfully updated Service table on DB.
- Doing so, self.manager.update_service must be called
- if 'self.binary == nova-compute', and this testcase checks on it.
- """
+ """Confirm compute updates their record of compute-service table."""
host = 'foo'
binary = 'nova-compute'
topic = 'compute1'
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index 8ed726c21..91bdfcc5a 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -23,8 +23,8 @@ from nova import context
from nova import db
from nova import exception
from nova import flags
-from nova import test
from nova import logging
+from nova import test
from nova import utils
from nova.api.ec2 import cloud
from nova.auth import manager
@@ -76,12 +76,12 @@ class LibvirtConnTestCase(test.TestCase):
'bridge': 'br101',
'instance_type': 'm1.small'}
- def _driver_dependent_test_setup(self):
- """
- Setup method.
- Call this method at the top of each testcase method,
- if the testcase is necessary libvirt and cheetah.
- """
+ def _driver_dependant_test_setup(self):
+ """Call this method at the top of each testcase method.
+
+ Checks if libvirt and cheetah, etc is installed.
+ Otherwise, skip testing."""
+
try:
global libvirt
global libxml2
@@ -92,10 +92,9 @@ class LibvirtConnTestCase(test.TestCase):
except ImportError, e:
logging.warn("""This test has not been done since """
"""using driver-dependent library Cheetah/libvirt/libxml2.""")
- raise e
+ raise
# inebitable mocks for calling
- #nova.virt.libvirt_conn.LibvirtConnection.__init__
obj = utils.import_object(FLAGS.firewall_driver)
fwmock = self.mox.CreateMock(obj)
self.mox.StubOutWithMock(libvirt_conn, 'utils',
@@ -258,51 +257,31 @@ class LibvirtConnTestCase(test.TestCase):
self.assertEquals(uri, testuri)
def test_get_vcpu_total(self):
- """
- Check if get_vcpu_total returns appropriate cpu value
- Connection/OS/driver differenct does not matter for this method,
- everyone can execute for checking.
- """
+ """Check if get_vcpu_total returns appropriate cpu value."""
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
self.assertTrue(0 < conn.get_vcpu_total())
- self.mox.UnsetStubs()
def test_get_memory_mb_total(self):
- """Check if get_memory_mb returns appropriate memory value"""
+ """Check if get_memory_mb returns appropriate memory value."""
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
self.assertTrue(0 < conn.get_memory_mb_total())
- self.mox.UnsetStubs()
-
- def test_get_local_gb_total(self):
- """Check if get_local_gb_total returns appropriate disk value"""
- # Note(masumotok): leave this b/c FLAGS.instances_path is inevitable..
- #try:
- # self._driver_dependent_test_setup()
- #except:
- # return
- #
- #self.mox.ReplayAll()
- #conn = libvirt_conn.LibvirtConnection(False)
- #self.assertTrue(0 < conn.get_local_gb_total())
- #self.mox.UnsetStubs()
- pass
def test_get_vcpu_used(self):
- """Check if get_local_gb_total returns appropriate disk value"""
+ """Check if get_local_gb_total returns appropriate disk value."""
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
@@ -321,52 +300,45 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
self.assertTrue(conn.get_vcpu_used() == 4)
- self.mox.UnsetStubs()
def test_get_memory_mb_used(self):
- """Check if get_memory_mb returns appropriate memory value"""
+ """Check if get_memory_mb returns appropriate memory value."""
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
self.assertTrue(0 < conn.get_memory_mb_used())
- self.mox.UnsetStubs()
-
- def test_get_local_gb_used(self):
- """Check if get_local_gb_total returns appropriate disk value"""
- # Note(masumotok): leave this b/c FLAGS.instances_path is inevitable
- #try:
- # self._driver_dependent_test_setup()
- #except:
- # return
-
- #self.mox.ReplayAll()
- #conn = libvirt_conn.LibvirtConnection(False)
- #self.assertTrue(0 < conn.get_local_gb_used())
- #self.mox.UnsetStubs()
- pass
def test_get_cpu_info_works_correctly(self):
- """
- Check if get_cpu_info works correctly.
- (in case libvirt.getCapabilities() works correctly)
- """
- xml = ("""x86_64Nehalem"""
- """Intel"""
- """"""
- """"""
- """"""
- """"""
- """"""
- """"""
- """""")
+ """Check if get_cpu_info works correctly as expected."""
+ xml = """
+ x86_64
+ Nehalem
+ Intel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection,
@@ -376,27 +348,34 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
self.assertTrue(0 < len(conn.get_cpu_info()))
- self.mox.UnsetStubs()
def test_get_cpu_info_inappropreate_xml(self):
- """
- Check if get_cpu_info raises exception
- in case libvirt.getCapabilities() returns wrong xml
- (in case of xml doesnt have tag)
- """
- xml = ("""x86_64Nehalem"""
- """Intel"""
- """"""
- """"""
- """"""
- """"""
- """"""
- """"""
- """""")
+ """Raise exception if given xml is inappropriate."""
+ xml = """
+ x86_64
+ Nehalem
+ Intel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection,
@@ -409,29 +388,34 @@ class LibvirtConnTestCase(test.TestCase):
conn.get_cpu_info()
except exception.Invalid, e:
c1 = (0 <= e.message.find('Invalid xml'))
- self.assertTrue(c1)
- self.mox.UnsetStubs()
+ self.assertTrue(c1)
def test_get_cpu_info_inappropreate_xml2(self):
- """
- Check if get_cpu_info raises exception
- in case libvirt.getCapabilities() returns wrong xml
- (in case of xml doesnt have inproper tag
- meaning missing "socket" attribute)
- """
- xml = ("""x86_64Nehalem"""
- """Intel"""
- """"""
- """"""
- """"""
- """"""
- """"""
- """"""
- """""")
-
+ """Raise exception if given xml is inappropriate(topology tag)."""
+
+ xml = """
+ x86_64
+ Nehalem
+ Intel
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ """
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection,
@@ -444,29 +428,12 @@ class LibvirtConnTestCase(test.TestCase):
conn.get_cpu_info()
except exception.Invalid, e:
c1 = (0 <= e.message.find('Invalid xml: topology'))
- self.assertTrue(c1)
- self.mox.UnsetStubs()
+ self.assertTrue(c1)
def test_update_available_resource_works_correctly(self):
- """
- In this method, vcpus/memory_mb/local_gb/vcpu_used/
- memory_mb_used/local_gb_used/hypervisor_type/
- hypervisor_version/cpu_info should be changed.
- Based on this specification, this testcase confirms
- if this method finishes successfully,
- meaning self.db.service_update must be called with dictinary
-
- {'vcpu':aaa, 'memory_mb':bbb, 'local_gb':ccc,
- 'vcpu_used':aaa, 'memory_mb_used':bbb, 'local_gb_sed':ccc,
- 'hypervisor_type':ddd, 'hypervisor_version':eee,
- 'cpu_info':fff}
-
- Since each value of above dict can be obtained through
- driver(different depends on environment),
- only dictionary keys are checked.
- """
+ """Confirm compute_service table is updated successfully."""
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
@@ -478,7 +445,9 @@ class LibvirtConnTestCase(test.TestCase):
host = 'foo'
binary = 'nova-compute'
- service_ref = {'id': 1, 'host': host, 'binary': binary,
+ service_ref = {'id': 1,
+ 'host': host,
+ 'binary': binary,
'topic': 'compute'}
self.mox.StubOutWithMock(db, 'service_get_all_by_topic')
@@ -491,15 +460,11 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
conn.update_available_resource(host)
- self.mox.UnsetStubs()
def test_update_resource_info_raise_exception(self):
- """
- This testcase confirms if no record found on Service
- table, exception can be raised.
- """
+ """Raise exception if no recorde found on services table."""
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
@@ -518,18 +483,19 @@ class LibvirtConnTestCase(test.TestCase):
msg = 'Cannot insert compute manager specific info'
c1 = (0 <= e.message.find(msg))
self.assertTrue(c1)
- self.mox.ResetAll()
def test_compare_cpu_works_correctly(self):
- """Calling libvirt.compute_cpu() and works correctly """
-
- t = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
- """"topology":{"cores":"%s", "threads":"%s", """
- """"sockets":"%s"}, "features":[%s]}""")
- cpu_info = t % ('x86', 'model', 'vendor', '2', '1', '4', '"tm"')
+ """Calling libvirt.compute_cpu() and works correctly."""
+ t = {}
+ t['arch'] = 'x86'
+ t['model'] = 'model'
+ t['vendor'] = 'Intel'
+ t['topology'] = {'cores': "2", "threads": "1", "sockets": "4"}
+ t['features'] = ["tm"]
+ cpu_info = utils.dumps(t)
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
@@ -542,20 +508,19 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
self.assertTrue(None == conn.compare_cpu(cpu_info))
- self.mox.UnsetStubs()
def test_compare_cpu_raises_exception(self):
- """
- Libvirt-related exception occurs when calling
- libvirt.compare_cpu().
- """
- t = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
- """"topology":{"cores":"%s", "threads":"%s", """
- """"sockets":"%s"}, "features":[%s]}""")
- cpu_info = t % ('x86', 'model', 'vendor', '2', '1', '4', '"tm"')
+ """Libvirt-related exception occurs when calling compare_cpu()."""
+ t = {}
+ t['arch'] = 'x86'
+ t['model'] = 'model'
+ t['vendor'] = 'Intel'
+ t['topology'] = {'cores': "2", "threads": "1", "sockets": "4"}
+ t['features'] = ["tm"]
+ cpu_info = utils.dumps(t)
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
@@ -567,18 +532,19 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
self.assertRaises(libvirt.libvirtError, conn.compare_cpu, cpu_info)
- self.mox.UnsetStubs()
def test_compare_cpu_no_compatibility(self):
- """libvirt.compare_cpu() return less than 0.(no compatibility)"""
-
- t = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
- """"topology":{"cores":"%s", "threads":"%s", """
- """"sockets":"%s"}, "features":[%s]}""")
- cpu_info = t % ('x86', 'model', 'vendor', '2', '1', '4', '"tm"')
+ """Libvirt.compare_cpu() return less than 0.(no compatibility)."""
+ t = {}
+ t['arch'] = 'x86'
+ t['model'] = 'model'
+ t['vendor'] = 'Intel'
+ t['topology'] = {'cores': "2", "threads": "1", "sockets": "4"}
+ t['features'] = ["tm"]
+ cpu_info = utils.dumps(t)
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
@@ -590,16 +556,14 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
self.assertRaises(exception.Invalid, conn.compare_cpu, cpu_info)
- self.mox.UnsetStubs()
def test_ensure_filtering_rules_for_instance_works_correctly(self):
- """ensure_filtering_rules_for_instance works as expected correctly"""
-
+ """ensure_filtering_rules_for_instance() works successfully."""
instance_ref = models.Instance()
instance_ref.__setitem__('id', 1)
try:
- nwmock, fwmock = self._driver_dependent_test_setup()
+ nwmock, fwmock = self._driver_dependant_test_setup()
except:
return
@@ -613,16 +577,14 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
conn.ensure_filtering_rules_for_instance(instance_ref)
- self.mox.UnsetStubs()
def test_ensure_filtering_rules_for_instance_timeout(self):
- """ensure_filtering_fules_for_instance finishes with timeout"""
-
+ """ensure_filtering_fules_for_instance() finishes with timeout."""
instance_ref = models.Instance()
instance_ref.__setitem__('id', 1)
try:
- nwmock, fwmock = self._driver_dependent_test_setup()
+ nwmock, fwmock = self._driver_dependant_test_setup()
except:
return
@@ -642,11 +604,9 @@ class LibvirtConnTestCase(test.TestCase):
except exception.Error, e:
c1 = (0 <= e.message.find('Timeout migrating for'))
self.assertTrue(c1)
- self.mox.UnsetStubs()
def test_live_migration_works_correctly(self):
- """_live_migration works as expected correctly """
-
+ """_live_migration() works as expected correctly."""
class dummyCall(object):
f = None
@@ -659,7 +619,7 @@ class LibvirtConnTestCase(test.TestCase):
ctxt = context.get_admin_context()
try:
- self._driver_dependent_test_setup()
+ self._driver_dependant_test_setup()
except:
return
@@ -681,13 +641,9 @@ class LibvirtConnTestCase(test.TestCase):
# Not setting post_method/recover_method in this testcase.
ret = conn._live_migration(ctxt, i_ref, i_ref['host'], '', '')
self.assertTrue(ret == None)
- self.mox.UnsetStubs()
def test_live_migration_raises_exception(self):
- """
- _live_migration raises exception, then this testcase confirms
- recovered method is called.
- """
+ """Confirms recover method is called when exceptions are raised."""
i_ref = models.Instance()
i_ref.__setitem__('id', 1)
i_ref.__setitem__('host', 'dummy')
@@ -697,7 +653,7 @@ class LibvirtConnTestCase(test.TestCase):
pass
try:
- nwmock, fwmock = self._driver_dependent_test_setup()
+ nwmock, fwmock = self._driver_dependant_test_setup()
except:
return
@@ -724,7 +680,6 @@ class LibvirtConnTestCase(test.TestCase):
conn._mlive_migration,
ctxt, instance_ref, dest,
'', dummy_recover_method)
- self.mox.UnsetStubs()
def tearDown(self):
super(LibvirtConnTestCase, self).tearDown()
diff --git a/nova/virt/fake.py b/nova/virt/fake.py
index 70ddd3aaf..069a424d1 100644
--- a/nova/virt/fake.py
+++ b/nova/virt/fake.py
@@ -326,59 +326,6 @@ class FakeConnection(object):
'username': 'fakeuser',
'password': 'fakepassword'}
- def get_cpu_info(self):
- """This method is supported only libvirt. """
- return
-
- def get_vcpu_total(self):
- """This method is supported only libvirt. """
- return
-
- def get_memory_mb_total(self):
- """This method is supported only libvirt. """
- return
-
- def get_local_gb_total(self):
- """This method is supported only libvirt. """
- return
-
- def get_vcpu_used(self):
- """This method is supported only libvirt. """
- return
-
- def get_memory_mb_used(self):
- """This method is supported only libvirt. """
- return
-
- def get_local_gb_used(self):
- """This method is supported only libvirt. """
- return
-
- def get_hypervisor_type(self):
- """This method is supported only libvirt.."""
- return
-
- def get_hypervisor_version(self):
- """This method is supported only libvirt.."""
- return
-
- def compare_cpu(self, xml):
- """This method is supported only libvirt.."""
- raise NotImplementedError('This method is supported only libvirt.')
-
- def ensure_filtering_rules_for_instance(self, instance_ref):
- """This method is supported only libvirt.."""
- return
-
- def live_migration(self, context, instance_ref, dest,
- post_method, recover_method):
- """This method is supported only libvirt.."""
- return
-
- def unfilter_instance(self, instance_ref):
- """This method is supported only libvirt.."""
- raise NotImplementedError('This method is supported only libvirt.')
-
def refresh_security_group_rules(self, security_group_id):
"""This method is called after a change to security groups.
@@ -428,20 +375,25 @@ class FakeConnection(object):
return True
def update_available_resource(self, ctxt, host):
- """This method is supported only libvirt. """
+ """This method is supported only by libvirt."""
return
def compare_cpu(self, xml):
- """This method is supported only libvirt.."""
- raise NotImplementedError('This method is supported only libvirt.')
+ """This method is supported only by libvirt."""
+ raise NotImplementedError('This method is supported only by libvirt.')
def ensure_filtering_rules_for_instance(self, instance_ref):
- """This method is supported only libvirt.."""
- raise NotImplementedError('This method is supported only libvirt.')
+ """This method is supported only by libvirt."""
+ raise NotImplementedError('This method is supported only by libvirt.')
+
+ def live_migration(self, context, instance_ref, dest,
+ post_method, recover_method):
+ """This method is supported only by libvirt."""
+ return
- def live_migration(self, context, instance_ref, dest):
- """This method is supported only libvirt.."""
- raise NotImplementedError('This method is supported only libvirt.')
+ def unfilter_instance(self, instance_ref):
+ """This method is supported only by libvirt."""
+ raise NotImplementedError('This method is supported only by libvirt.')
class FakeInstance(object):
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index d39836e72..934aed960 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -36,7 +36,6 @@ Supports KVM, QEMU, UML, and XEN.
"""
-import json
import os
import shutil
import random
@@ -105,7 +104,7 @@ flags.DEFINE_string('firewall_driver',
'Firewall driver (defaults to iptables)')
flags.DEFINE_string('cpuinfo_xml_template',
utils.abspath('virt/cpuinfo.xml.template'),
- 'CpuInfo XML Template (used only live migration now)')
+ 'CpuInfo XML Template (Used only live migration now)')
flags.DEFINE_string('live_migration_uri',
"qemu+tcp://%s/system",
'Define protocol used by live_migration feature')
@@ -851,23 +850,46 @@ class LibvirtConnection(object):
return interfaces
def get_vcpu_total(self):
- """ Get vcpu number of physical computer. """
+ """Get vcpu number of physical computer.
+
+ :returns: the number of cpu core.
+
+ """
+
return open('/proc/cpuinfo').read().count('processor')
def get_memory_mb_total(self):
- """Get the total memory size(MB) of physical computer ."""
+ """Get the total memory size(MB) of physical computer.
+
+ :returns: the total amount of memory(MB).
+
+ """
+
meminfo = open('/proc/meminfo').read().split()
idx = meminfo.index('MemTotal:')
# transforming kb to mb.
return int(meminfo[idx + 1]) / 1024
def get_local_gb_total(self):
- """Get the total hdd size(GB) of physical computer ."""
+ """Get the total hdd size(GB) of physical computer.
+
+ :returns:
+ The total amount of HDD(GB).
+ Note that this value shows a partition where
+ NOVA-INST-DIR/instances mounts.
+
+ """
+
hddinfo = os.statvfs(FLAGS.instances_path)
return hddinfo.f_frsize * hddinfo.f_blocks / 1024 / 1024 / 1024
def get_vcpu_used(self):
- """ Get vcpu available number of physical computer. """
+ """ Get vcpu usage number of physical computer.
+
+ :returns: The total number of vcpu that currently used.
+
+ """
+
total = 0
for i in self._conn.listDomainsID():
dom = self._conn.lookupByID(i)
@@ -875,7 +897,12 @@ class LibvirtConnection(object):
return total
def get_memory_mb_used(self):
- """Get the free memory size(MB) of physical computer."""
+ """Get the free memory size(MB) of physical computer.
+
+ :returns: the total usage of memory(MB).
+
+ """
+
m = open('/proc/meminfo').read().split()
idx1 = m.index('MemFree:')
idx2 = m.index('Buffers:')
@@ -884,21 +911,47 @@ class LibvirtConnection(object):
return self.get_memory_mb_total() - avail
def get_local_gb_used(self):
- """Get the free hdd size(GB) of physical computer ."""
+ """Get the free hdd size(GB) of physical computer.
+
+ :returns:
+ The total usage of HDD(GB).
+ Note that this value shows a partition where
+ NOVA-INST-DIR/instances mounts.
+
+ """
+
hddinfo = os.statvfs(FLAGS.instances_path)
avail = hddinfo.f_frsize * hddinfo.f_bavail / 1024 / 1024 / 1024
return self.get_local_gb_total() - avail
def get_hypervisor_type(self):
- """ Get hypervisor type """
+ """Get hypervisor type.
+
+ :returns: hypervisor type (ex. qemu)
+
+ """
+
return self._conn.getType()
def get_hypervisor_version(self):
- """ Get hypervisor version """
+ """Get hypervisor version.
+
+ :returns: hypervisor version (ex. 12003)
+
+ """
+
return self._conn.getVersion()
def get_cpu_info(self):
- """ Get cpuinfo information """
+ """Get cpuinfo information.
+
+ Obtains cpu feature from virConnect.getCapabilities,
+ and returns as a json string.
+
+ :return: see above description
+
+ """
+
xml = self._conn.getCapabilities()
xml = libxml2.parseDoc(xml)
nodes = xml.xpathEval('//cpu')
@@ -931,17 +984,9 @@ class LibvirtConnection(object):
for nodes in feature_nodes:
features.append(nodes.get_properties().getContent())
- template = ("""{"arch":"%s", "model":"%s", "vendor":"%s", """
- """"topology":{"cores":"%s", "threads":"%s", """
- """"sockets":"%s"}, "features":[%s]}""")
- f = ['"%s"' % x for x in features]
- return template % (cpu_info['arch'],
- cpu_info['model'],
- cpu_info['vendor'],
- topology['cores'],
- topology['sockets'],
- topology['threads'],
- ', '.join(f))
+ cpu_info['topology'] = topology
+ cpu_info['features'] = features
+ return utils.dumps(cpu_info)
def block_stats(self, instance_name, disk):
"""
@@ -974,12 +1019,16 @@ class LibvirtConnection(object):
self.firewall_driver.refresh_security_group_members(security_group_id)
def update_available_resource(self, ctxt, host):
- """
- Update compute manager resource info on Service table.
+ """Updates compute manager resource info on ComputeService table.
+
This method is called when nova-coompute launches, and
whenever admin executes "nova-manage service update_resource".
+ :param ctxt: security context
+ :param host: hostname that compute manager is currently running
+
"""
+
try:
service_ref = db.service_get_all_compute_by_host(ctxt, host)[0]
except exception.NotFound:
@@ -1008,44 +1057,44 @@ class LibvirtConnection(object):
db.compute_service_update(ctxt, compute_service_ref[0]['id'], dic)
def compare_cpu(self, cpu_info):
- """
- Check the host cpu is compatible to a cpu given by xml.
+ """Checks the host cpu is compatible to a cpu given by xml.
+
"xml" must be a part of libvirt.openReadonly().getCapabilities().
return values follows by virCPUCompareResult.
if 0 > return value, do live migration.
-
'http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult'
+
+ :param cpu_info: json string that shows cpu feature(see get_cpu_info())
+ :returns:
+ None. if given cpu info is not compatible to this server,
+ raise exception.
+
"""
- msg = _('Checking cpu_info: instance was launched this cpu.\n: %s ')
- LOG.info(msg % cpu_info)
- dic = json.loads(cpu_info)
- xml = str(Template(self.cpuinfo_xml, searchList=dic))
- msg = _('to xml...\n: %s ')
- LOG.info(msg % xml)
- url = 'http://libvirt.org/html/libvirt-libvirt.html'
- url += '#virCPUCompareResult\n'
- msg = 'CPU does not have compativility.\n'
- msg += 'result:%s \n'
- msg += 'Refer to %s'
- msg = _(msg)
+ LOG.info(_('Checking cpu_info: instance was launched this cpu.\n%s')
+ % cpu_info)
+ dic = utils.loads(cpu_info)
+ xml = str(Template(self.cpuinfo_xml, searchList=dic))
+ LOG.info(_('to xml...\n:%s ' % xml))
+ u = "http://libvirt.org/html/libvirt-libvirt.html#virCPUCompareResult"
+ m = _("CPU doesn't have compatibility.\n\n%(ret)s\n\nRefer to %(u)s")
# unknown character exists in xml, then libvirt complains
try:
ret = self._conn.compareCPU(xml, 0)
except libvirt.libvirtError, e:
- LOG.error(msg % (e.message, url))
- raise e
+ ret = e.message
+ LOG.error(m % locals())
+ raise
if ret <= 0:
- raise exception.Invalid(msg % (ret, url))
+ raise exception.Invalid(m % locals())
return
def ensure_filtering_rules_for_instance(self, instance_ref):
- """
- Setting up inevitable filtering rules on compute node,
- and waiting for its completion.
+ """Setting up filtering rules and waiting for its completion.
+
To migrate an instance, filtering rules to hypervisors
and firewalls are inevitable on destination host.
( Waiting only for filterling rules to hypervisor,
@@ -1062,9 +1111,12 @@ class LibvirtConnection(object):
Don't use thread for this method since migration should
not be started when setting-up filtering rules operations
are not completed.
+
+ :params instance_ref: nova.db.sqlalchemy.models.Instance object
+
"""
- # Tf any instances never launch at destination host,
+ # If any instances never launch at destination host,
# basic-filtering must be set here.
self.firewall_driver.setup_basic_filtering(instance_ref)
# setting up n)ova-instance-instance-xx mainly.
@@ -1088,16 +1140,42 @@ class LibvirtConnection(object):
def live_migration(self, ctxt, instance_ref, dest,
post_method, recover_method):
+ """Spawning live_migration operation for distributing high-load.
+
+ :params ctxt: security context
+ :params instance_ref:
+ nova.db.sqlalchemy.models.Instance object
+ instance object that is migrated.
+ :params dest: destination host
+ :params post_method:
+ post operation method.
+ expected nova.compute.manager.post_live_migration.
+ :params recover_method:
+ recovery method when any exception occurs.
+ expected nova.compute.manager.recover_live_migration.
+
"""
- Just spawning live_migration operation for
- distributing high-load.
- """
+
greenthread.spawn(self._live_migration, ctxt, instance_ref, dest,
post_method, recover_method)
def _live_migration(self, ctxt, instance_ref, dest,
post_method, recover_method):
- """ Do live migration."""
+ """Do live migration.
+
+ :params ctxt: security context
+ :params instance_ref:
+ nova.db.sqlalchemy.models.Instance object
+ instance object that is migrated.
+ :params dest: destination host
+ :params post_method:
+ post operation method.
+ expected nova.compute.manager.post_live_migration.
+ :params recover_method:
+ recovery method when any exception occurs.
+ expected nova.compute.manager.recover_live_migration.
+
+ """
# Do live migration.
try:
@@ -1122,7 +1200,7 @@ class LibvirtConnection(object):
except Exception, e:
recover_method(ctxt, instance_ref)
- raise e
+ raise
# Waiting for completion of live_migration.
timer = utils.LoopingCall(f=None)
@@ -1139,7 +1217,7 @@ class LibvirtConnection(object):
timer.start(interval=0.5, now=True)
def unfilter_instance(self, instance_ref):
- """See comments of same method in firewall_driver"""
+ """See comments of same method in firewall_driver."""
self.firewall_driver.unfilter_instance(instance_ref)
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index 3dd7d6e94..0e12a4587 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -231,25 +231,25 @@ class XenAPIConnection(object):
'password': FLAGS.xenapi_connection_password}
def update_available_resource(self, ctxt, host):
- """This method is supported only libvirt. """
+ """This method is supported only by libvirt."""
return
def compare_cpu(self, xml):
- """This method is supported only libvirt.."""
- raise NotImplementedError('This method is supported only libvirt.')
+ """This method is supported only by libvirt."""
+ raise NotImplementedError('This method is supported only by libvirt.')
def ensure_filtering_rules_for_instance(self, instance_ref):
- """This method is supported only libvirt.."""
+ """This method is supported only libvirt."""
return
def live_migration(self, context, instance_ref, dest,
post_method, recover_method):
- """This method is supported only libvirt.."""
+ """This method is supported only by libvirt."""
return
def unfilter_instance(self, instance_ref):
- """This method is supported only libvirt.."""
- raise NotImplementedError('This method is supported only libvirt.')
+ """This method is supported only by libvirt."""
+ raise NotImplementedError('This method is supported only by libvirt.')
class XenAPISession(object):
diff --git a/nova/volume/driver.py b/nova/volume/driver.py
index b74c74197..fbc52a598 100644
--- a/nova/volume/driver.py
+++ b/nova/volume/driver.py
@@ -140,7 +140,7 @@ class VolumeDriver(object):
def check_for_export(self, context, volume_id):
"""Make sure volume is exported."""
- return True
+ raise NotImplementedError()
class AOEDriver(VolumeDriver):
@@ -229,9 +229,9 @@ class AOEDriver(VolumeDriver):
break
if not exported:
# Instance will be terminated in this case.
- desc = _("""Cannot confirm exported volume id:%(volume_id)s."""
- """vblade process for e%(shelf_id)s.%(blade_id)s """
- """isn't running.""") % locals()
+ desc = _("Cannot confirm exported volume id:%(volume_id)s."
+ "vblade process for e%(shelf_id)s.%(blade_id)s "
+ "isn't running.") % locals()
raise exception.ProcessExecutionError(out, _err, cmd=cmd,
description=desc)
@@ -373,9 +373,9 @@ class ISCSIDriver(VolumeDriver):
# Instances remount read-only in this case.
# /etc/init.d/iscsitarget restart and rebooting nova-volume
# is better since ensure_export() works at boot time.
- logging.error(_("""Cannot confirm exported volume """
- """id:%(volume_id)s.""") % locals())
- raise e
+ logging.error(_("Cannot confirm exported volume "
+ "id:%(volume_id)s.") % locals())
+ raise
class FakeISCSIDriver(ISCSIDriver):
--
cgit
From c6b2d07f47004576fa386a6d270203b1d7937664 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Wed, 23 Feb 2011 00:15:39 +0900
Subject: Fix tiny mitakes! (remove unnecessary comment, etc)
---
nova/tests/test_scheduler.py | 2 +-
nova/tests/test_volume.py | 3 ---
nova/virt/libvirt_conn.py | 2 +-
3 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index 301106848..47a6d0e82 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -258,10 +258,10 @@ class SimpleDriverTestCase(test.TestCase):
inst['project_id'] = self.project.id
inst['instance_type'] = 'm1.tiny'
inst['mac_address'] = utils.generate_mac()
+ inst['vcpus'] = kwargs.get('vcpus', 1)
inst['ami_launch_index'] = 0
inst['availability_zone'] = kwargs.get('availability_zone', None)
inst['host'] = kwargs.get('host', 'dummy')
- inst['vcpus'] = kwargs.get('vcpus', 4)
inst['memory_mb'] = kwargs.get('memory_mb', 20)
inst['local_gb'] = kwargs.get('local_gb', 30)
inst['launched_on'] = kwargs.get('launghed_on', 'dummy')
diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py
index 6ae075caa..e8b4ceee8 100644
--- a/nova/tests/test_volume.py
+++ b/nova/tests/test_volume.py
@@ -318,9 +318,6 @@ class ISCSITestCase(DriverTestCase):
mountpoint = "/dev/sd" + chr((ord('b') + index))
db.volume_attached(self.context, vol_ref['id'], self.instance_id,
mountpoint)
- #iscsi_target = db.volume_allocate_iscsi_target(self.context,
- # vol_ref['id'],
- # vol_ref['host'])
volume_id_list.append(vol_ref['id'])
return volume_id_list
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 934aed960..118ea13e5 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -888,7 +888,7 @@ class LibvirtConnection(object):
:returns: The total number of vcpu that currently used.
- """
+ """
total = 0
for i in self._conn.listDomainsID():
--
cgit
From 485a6c5a9502679bc5ecf02f8e758170ac0335dc Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Wed, 23 Feb 2011 01:20:39 +0900
Subject: Fixed some docstring
---
nova/scheduler/manager.py | 2 +-
nova/tests/test_scheduler.py | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index 783594c6f..cd5012fd5 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -59,7 +59,7 @@ class SchedulerManager(manager.Manager):
try:
host = getattr(self.driver, driver_method)(elevated, *args,
**kwargs)
- except AttributeError, e:
+ except AttributeError:
host = self.driver.schedule(elevated, topic, *args, **kwargs)
rpc.cast(context,
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index 47a6d0e82..71e524bca 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -60,6 +60,7 @@ class SchedulerTestCase(test.TestCase):
self.flags(scheduler_driver='nova.tests.test_scheduler.TestDriver')
def _create_compute_service(self):
+ """Create compute-manager(ComputeService and Service record)."""
ctxt = context.get_admin_context()
dic = {'host': 'dummy', 'binary': 'nova-compute', 'topic': 'compute',
'report_count': 0, 'availability_zone': 'dummyzone'}
@@ -150,7 +151,7 @@ class SchedulerTestCase(test.TestCase):
db.service_destroy(ctxt, s_ref['id'])
def test_show_host_resources_works_correctly(self):
- """show_host_resources() works correctly as expected."""
+ """Show_host_resources() works correctly as expected."""
scheduler = manager.SchedulerManager()
ctxt = context.get_admin_context()
--
cgit
From d27aa094a168dcfb486bbd49ef61be78bd5a50f2 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Wed, 23 Feb 2011 17:28:38 -0800
Subject: remove extra flag in admin tests
---
smoketests/admin_smoketests.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/smoketests/admin_smoketests.py b/smoketests/admin_smoketests.py
index 86a7f600d..cb3010965 100644
--- a/smoketests/admin_smoketests.py
+++ b/smoketests/admin_smoketests.py
@@ -35,10 +35,7 @@ from smoketests import flags
from smoketests import base
-SUITE_NAMES = '[user]'
-
FLAGS = flags.FLAGS
-flags.DEFINE_string('suite', None, 'Specific test suite to run ' + SUITE_NAMES)
# TODO(devamcar): Use random tempfile
ZIP_FILENAME = '/tmp/nova-me-x509.zip'
--
cgit
From 79d9e06d79264d614a465971dd43176bcf190703 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Wed, 23 Feb 2011 22:40:50 -0800
Subject: more smoketest fixes
---
smoketests/netadmin_smoketests.py | 5 -----
smoketests/sysadmin_smoketests.py | 6 ++++--
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/smoketests/netadmin_smoketests.py b/smoketests/netadmin_smoketests.py
index 38beb8fdc..16113e4a9 100644
--- a/smoketests/netadmin_smoketests.py
+++ b/smoketests/netadmin_smoketests.py
@@ -137,11 +137,6 @@ class SecurityGroupTests(base.UserSmokeTestCase):
if not self.wait_for_running(self.data['instance']):
self.fail('instance failed to start')
self.data['instance'].update()
- if not self.wait_for_ping(self.data['instance'].private_dns_name):
- self.fail('could not ping instance')
- if not self.wait_for_ssh(self.data['instance'].private_dns_name,
- TEST_KEY):
- self.fail('could not ssh to instance')
def test_003_can_authorize_security_group_ingress(self):
self.assertTrue(self.conn.authorize_security_group(TEST_GROUP,
diff --git a/smoketests/sysadmin_smoketests.py b/smoketests/sysadmin_smoketests.py
index e3b84d3d3..3b267bc65 100644
--- a/smoketests/sysadmin_smoketests.py
+++ b/smoketests/sysadmin_smoketests.py
@@ -191,7 +191,7 @@ class VolumeTests(base.UserSmokeTestCase):
self.assertEqual(volume.size, 1)
self.data['volume'] = volume
# Give network time to find volume.
- time.sleep(10)
+ time.sleep(5)
def test_002_can_attach_volume(self):
volume = self.data['volume']
@@ -204,6 +204,8 @@ class VolumeTests(base.UserSmokeTestCase):
else:
self.fail('cannot attach volume with state %s' % volume.status)
+ # Give volume some time to be ready.
+ time.sleep(5)
volume.attach(self.data['instance'].id, self.device)
# wait
@@ -218,7 +220,7 @@ class VolumeTests(base.UserSmokeTestCase):
self.assertTrue(volume.status.startswith('in-use'))
# Give instance time to recognize volume.
- time.sleep(10)
+ time.sleep(5)
def test_003_can_mount_volume(self):
ip = self.data['instance'].private_dns_name
--
cgit
From fc465b2a9f1c639e44e59c43cbe4d9a9a188f396 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Thu, 24 Feb 2011 00:25:21 -0800
Subject: fix check for existing port 22 rule
---
smoketests/netadmin_smoketests.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/smoketests/netadmin_smoketests.py b/smoketests/netadmin_smoketests.py
index 16113e4a9..54432242c 100644
--- a/smoketests/netadmin_smoketests.py
+++ b/smoketests/netadmin_smoketests.py
@@ -74,8 +74,10 @@ class AddressTests(base.UserSmokeTestCase):
groups = self.conn.get_all_security_groups(['default'])
for rule in groups[0].rules:
if (rule.ip_protocol == 'tcp' and
- rule.from_port <= 22 and rule.to_port >= 22):
+ int(rule.from_port) <= 22 and
+ int(rule.to_port) >= 22):
ssh_authorized = True
+ break
if not ssh_authorized:
self.conn.authorize_security_group('default',
ip_protocol='tcp',
--
cgit
From f7beae47ca505443eb86ea1a4fba6b47c1658755 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Thu, 24 Feb 2011 17:07:59 -0800
Subject: IPV6 FlatManager changes
---
nova/db/sqlalchemy/api.py | 11 ++--
.../versions/007_add_ipv6_flatmanager.py | 75 ++++++++++++++++++++++
nova/db/sqlalchemy/models.py | 3 +
nova/network/manager.py | 35 +++++++++-
nova/virt/interfaces.template | 16 +++--
nova/virt/libvirt_conn.py | 27 +++++---
6 files changed, 149 insertions(+), 18 deletions(-)
create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index d8751bef4..9bc59de60 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -833,10 +833,13 @@ def instance_get_fixed_address_v6(context, instance_id):
session = get_session()
with session.begin():
instance_ref = instance_get(context, instance_id, session=session)
- network_ref = network_get_by_instance(context, instance_id)
- prefix = network_ref.cidr_v6
- mac = instance_ref.mac_address
- return utils.to_global_ipv6(prefix, mac)
+ if 'nova.network.manager.FlatManager' == FLAGS.network_manager:
+ return instance_ref.fixed_ip['addressv6']
+ else:
+ network_ref = network_get_by_instance(context, instance_id)
+ prefix = network_ref.cidr_v6
+ mac = instance_ref.mac_address
+ return utils.to_global_ipv6(prefix, mac)
@require_context
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
new file mode 100644
index 000000000..2951cbc0a
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
@@ -0,0 +1,75 @@
+# Copyright 2010 OpenStack LLC.
+# 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.
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import log as logging
+
+
+meta = MetaData()
+
+
+# Table stub-definitions
+# Just for the ForeignKey and column creation to succeed, these are not the
+# actual definitions of instances or services.
+#
+networks = Table('networks', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+fixed_ips = Table('fixed_ips', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+#
+# New Tables
+#
+# None
+
+#
+# Tables to alter
+#
+# None
+
+#
+# Columns to add to existing tables
+#
+
+networks_gatewayv6 = Column(
+ 'gatewayv6',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False))
+
+networks_netmaskv6 = Column(
+ 'netmaskv6',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False))
+
+fixed_ips_addressv6 = Column(
+ 'addressv6',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False))
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ # Add columns to existing tables
+ networks.create_column(networks_gatewayv6)
+ networks.create_column(networks_netmaskv6)
+ fixed_ips.create_column(fixed_ips_addressv6)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 1882efeba..4fa4d443c 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -385,6 +385,8 @@ class Network(BASE, NovaBase):
ra_server = Column(String(255))
+ gatewayv6 = Column(String(255))
+ netmaskv6 = Column(String(255))
netmask = Column(String(255))
bridge = Column(String(255))
gateway = Column(String(255))
@@ -425,6 +427,7 @@ class FixedIp(BASE, NovaBase):
__tablename__ = 'fixed_ips'
id = Column(Integer, primary_key=True)
address = Column(String(255))
+ addressv6 = Column(String(255))
network_id = Column(Integer, ForeignKey('networks.id'), nullable=True)
network = relationship(Network, backref=backref('fixed_ips'))
instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 12a0c5018..61b5dc07f 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -361,9 +361,11 @@ class FlatManager(NetworkManager):
fixed_net = IPy.IP(cidr)
fixed_net_v6 = IPy.IP(cidr_v6)
significant_bits_v6 = 64
+ network_size_v6 = 1 << 64
count = 1
for index in range(num_networks):
start = index * network_size
+ start_v6 = index * network_size_v6
significant_bits = 32 - int(math.log(network_size, 2))
cidr = "%s/%s" % (fixed_net[start], significant_bits)
project_net = IPy.IP(cidr)
@@ -382,14 +384,45 @@ class FlatManager(NetworkManager):
count += 1
if(FLAGS.use_ipv6):
- cidr_v6 = "%s/%s" % (fixed_net_v6[0], significant_bits_v6)
+ cidr_v6 = "%s/%s" % (fixed_net_v6[start_v6],
+ significant_bits_v6)
net['cidr_v6'] = cidr_v6
+ project_net_v6 = IPy.IP(cidr_v6)
+ net['gatewayv6'] = str(project_net_v6[1])
+ net['netmaskv6'] = str(project_net_v6.prefixlen())
network_ref = self.db.network_create_safe(context, net)
if network_ref:
self._create_fixed_ips(context, network_ref['id'])
+ def _create_fixed_ips(self, context, network_id):
+ """Create all fixed ips for network."""
+ network_ref = self.db.network_get(context, network_id)
+ # NOTE(vish): Should these be properties of the network as opposed
+ # to properties of the manager class?
+ bottom_reserved = self._bottom_reserved_ips
+ top_reserved = self._top_reserved_ips
+ project_net = IPy.IP(network_ref['cidr'])
+
+ if(FLAGS.use_ipv6):
+ project_net_v6 = IPy.IP(network_ref['cidr_v6'])
+
+ num_ips = len(project_net)
+ addressv6 = None
+ for index in range(num_ips):
+ address = str(project_net[index])
+ if(FLAGS.use_ipv6):
+ addressv6 = str(project_net_v6[index])
+ if index < bottom_reserved or num_ips - index < top_reserved:
+ reserved = True
+ else:
+ reserved = False
+ self.db.fixed_ip_create(context, {'network_id': network_id,
+ 'address': address,
+ 'addressv6': addressv6,
+ 'reserved': reserved})
+
def get_network_host(self, context):
"""Get the network host for the current context."""
network_ref = self.db.network_get_by_bridge(context,
diff --git a/nova/virt/interfaces.template b/nova/virt/interfaces.template
index 87b92b84a..1db745f9f 100644
--- a/nova/virt/interfaces.template
+++ b/nova/virt/interfaces.template
@@ -8,10 +8,16 @@ iface lo inet loopback
# The primary network interface
auto eth0
iface eth0 inet static
- address %(address)s
- netmask %(netmask)s
- broadcast %(broadcast)s
- gateway %(gateway)s
- dns-nameservers %(dns)s
+ address ${address}
+ netmask ${netmask}
+ broadcast ${broadcast}
+ gateway ${gateway}
+ dns-nameservers ${dns}
+#if $use_ipv6
+iface eth0 inet6 static
+ address ${addressv6}
+ netmask ${netmaskv6}
+ gateway ${gatewayv6}
+#end if
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 4e0fd106f..b7712f76e 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -146,6 +146,7 @@ class LibvirtConnection(object):
self.libvirt_uri = self.get_uri()
self.libvirt_xml = open(FLAGS.libvirt_xml_template).read()
+ self.interfaces_xml = open(FLAGS.injected_network_template).read()
self._wrapped_conn = None
self.read_only = read_only
@@ -628,17 +629,27 @@ class LibvirtConnection(object):
inst['id'])
if network_ref['injected']:
admin_context = context.get_admin_context()
- address = db.instance_get_fixed_address(admin_context, inst['id'])
+ address = db.instance_get_fixed_address(admin_context,
+ inst['id'])
+ addressv6 = db.instance_get_fixed_address_v6(admin_context,
+ inst['id'])
ra_server = network_ref['ra_server']
if not ra_server:
ra_server = "fd00::"
- with open(FLAGS.injected_network_template) as f:
- net = f.read() % {'address': address,
- 'netmask': network_ref['netmask'],
- 'gateway': network_ref['gateway'],
- 'broadcast': network_ref['broadcast'],
- 'dns': network_ref['dns'],
- 'ra_server': ra_server}
+
+ interfaces_info = {'address': address,
+ 'netmask': network_ref['netmask'],
+ 'gateway': network_ref['gateway'],
+ 'broadcast': network_ref['broadcast'],
+ 'dns': network_ref['dns'],
+ 'ra_server': ra_server,
+ 'addressv6': addressv6,
+ 'gatewayv6': network_ref['gatewayv6'],
+ 'netmaskv6': network_ref['netmaskv6'],
+ 'use_ipv6': FLAGS.use_ipv6}
+
+ net = str(Template(self.interfaces_xml,
+ searchList=[interfaces_info]))
if key or net:
inst_name = inst['name']
img_id = inst.image_id
--
cgit
From 498638ce36228615ecf8d98f99c0227f4f86963d Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Thu, 24 Feb 2011 17:17:42 -0800
Subject: make smoketests run with nose
---
run_tests.py | 2 +
smoketests/admin_smoketests.py | 95 ----------
smoketests/netadmin_smoketests.py | 191 --------------------
smoketests/public_network_smoketests.py | 5 -
smoketests/run_tests.py | 297 ++++++++++++++++++++++++++++++++
smoketests/sysadmin_smoketests.py | 295 -------------------------------
smoketests/test_admin.py | 91 ++++++++++
smoketests/test_netadmin.py | 184 ++++++++++++++++++++
smoketests/test_sysadmin.py | 287 ++++++++++++++++++++++++++++++
9 files changed, 861 insertions(+), 586 deletions(-)
delete mode 100644 smoketests/admin_smoketests.py
delete mode 100644 smoketests/netadmin_smoketests.py
create mode 100644 smoketests/run_tests.py
delete mode 100644 smoketests/sysadmin_smoketests.py
create mode 100644 smoketests/test_admin.py
create mode 100644 smoketests/test_netadmin.py
create mode 100644 smoketests/test_sysadmin.py
diff --git a/run_tests.py b/run_tests.py
index 3c8d410e1..d5d8acd16 100644
--- a/run_tests.py
+++ b/run_tests.py
@@ -60,6 +60,8 @@ import os
import unittest
import sys
+gettext.install('nova', unicode=1)
+
from nose import config
from nose import core
from nose import result
diff --git a/smoketests/admin_smoketests.py b/smoketests/admin_smoketests.py
deleted file mode 100644
index cb3010965..000000000
--- a/smoketests/admin_smoketests.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# 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 random
-import sys
-import unittest
-import zipfile
-
-# 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)
-
-from nova import adminclient
-from smoketests import flags
-from smoketests import base
-
-
-FLAGS = flags.FLAGS
-
-# TODO(devamcar): Use random tempfile
-ZIP_FILENAME = '/tmp/nova-me-x509.zip'
-
-TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
-TEST_USERNAME = '%suser' % TEST_PREFIX
-TEST_PROJECTNAME = '%sproject' % TEST_PREFIX
-
-
-class AdminSmokeTestCase(base.SmokeTestCase):
- def setUp(self):
- self.admin = adminclient.NovaAdminClient(
- access_key=os.getenv('EC2_ACCESS_KEY'),
- secret_key=os.getenv('EC2_SECRET_KEY'),
- clc_url=os.getenv('EC2_URL'),
- region=FLAGS.region)
-
-
-class UserTests(AdminSmokeTestCase):
- """ Test admin credentials and user creation. """
-
- def test_001_admin_can_connect(self):
- conn = self.admin.connection_for('admin', 'admin')
- self.assert_(conn)
-
- def test_002_admin_can_create_user(self):
- user = self.admin.create_user(TEST_USERNAME)
- self.assertEqual(user.username, TEST_USERNAME)
-
- def test_003_admin_can_create_project(self):
- project = self.admin.create_project(TEST_PROJECTNAME,
- TEST_USERNAME)
- self.assertEqual(project.projectname, TEST_PROJECTNAME)
-
- def test_004_user_can_download_credentials(self):
- buf = self.admin.get_zip(TEST_USERNAME, TEST_PROJECTNAME)
- output = open(ZIP_FILENAME, 'w')
- output.write(buf)
- output.close()
-
- zip = zipfile.ZipFile(ZIP_FILENAME, 'a', zipfile.ZIP_DEFLATED)
- bad = zip.testzip()
- zip.close()
-
- self.failIf(bad)
-
- def test_999_tearDown(self):
- self.admin.delete_project(TEST_PROJECTNAME)
- self.admin.delete_user(TEST_USERNAME)
- try:
- os.remove(ZIP_FILENAME)
- except:
- pass
-
-if __name__ == "__main__":
- suites = {'user': unittest.makeSuite(UserTests)}
- sys.exit(base.run_tests(suites))
diff --git a/smoketests/netadmin_smoketests.py b/smoketests/netadmin_smoketests.py
deleted file mode 100644
index 54432242c..000000000
--- a/smoketests/netadmin_smoketests.py
+++ /dev/null
@@ -1,191 +0,0 @@
-# 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 commands
-import os
-import random
-import sys
-import time
-import unittest
-
-# 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)
-
-from smoketests import flags
-from smoketests import base
-
-
-FLAGS = flags.FLAGS
-
-TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
-TEST_BUCKET = '%s_bucket' % TEST_PREFIX
-TEST_KEY = '%s_key' % TEST_PREFIX
-TEST_GROUP = '%s_group' % TEST_PREFIX
-
-
-class AddressTests(base.UserSmokeTestCase):
- def test_000_setUp(self):
- self.create_key_pair(self.conn, TEST_KEY)
- reservation = self.conn.run_instances(FLAGS.test_image,
- instance_type='m1.tiny',
- key_name=TEST_KEY)
- self.data['instance'] = reservation.instances[0]
- if not self.wait_for_running(self.data['instance']):
- self.fail('instance failed to start')
- self.data['instance'].update()
- if not self.wait_for_ping(self.data['instance'].private_dns_name):
- self.fail('could not ping instance')
- if not self.wait_for_ssh(self.data['instance'].private_dns_name,
- TEST_KEY):
- self.fail('could not ssh to instance')
-
- def test_001_can_allocate_floating_ip(self):
- result = self.conn.allocate_address()
- self.assertTrue(hasattr(result, 'public_ip'))
- self.data['public_ip'] = result.public_ip
-
- def test_002_can_associate_ip_with_instance(self):
- result = self.conn.associate_address(self.data['instance'].id,
- self.data['public_ip'])
- self.assertTrue(result)
-
- def test_003_can_ssh_with_public_ip(self):
- ssh_authorized = False
- groups = self.conn.get_all_security_groups(['default'])
- for rule in groups[0].rules:
- if (rule.ip_protocol == 'tcp' and
- int(rule.from_port) <= 22 and
- int(rule.to_port) >= 22):
- ssh_authorized = True
- break
- if not ssh_authorized:
- self.conn.authorize_security_group('default',
- ip_protocol='tcp',
- from_port=22,
- to_port=22)
- try:
- if not self.wait_for_ssh(self.data['public_ip'], TEST_KEY):
- self.fail('could not ssh to public ip')
- finally:
- if not ssh_authorized:
- self.conn.revoke_security_group('default',
- ip_protocol='tcp',
- from_port=22,
- to_port=22)
-
- def test_004_can_disassociate_ip_from_instance(self):
- result = self.conn.disassociate_address(self.data['public_ip'])
- self.assertTrue(result)
-
- def test_005_can_deallocate_floating_ip(self):
- result = self.conn.release_address(self.data['public_ip'])
- self.assertTrue(result)
-
- def test_999_tearDown(self):
- self.delete_key_pair(self.conn, TEST_KEY)
- self.conn.terminate_instances([self.data['instance'].id])
-
-
-class SecurityGroupTests(base.UserSmokeTestCase):
-
- def __public_instance_is_accessible(self):
- id_url = "latest/meta-data/instance-id"
- options = "-s --max-time 1"
- command = "curl %s %s/%s" % (options, self.data['public_ip'], id_url)
- instance_id = commands.getoutput(command).strip()
- if not instance_id:
- return False
- if instance_id != self.data['instance'].id:
- raise Exception("Wrong instance id")
- return True
-
- def test_001_can_create_security_group(self):
- self.conn.create_security_group(TEST_GROUP, description='test')
-
- groups = self.conn.get_all_security_groups()
- self.assertTrue(TEST_GROUP in [group.name for group in groups])
-
- def test_002_can_launch_instance_in_security_group(self):
- with open("proxy.sh") as f:
- user_data = f.read()
- self.create_key_pair(self.conn, TEST_KEY)
- reservation = self.conn.run_instances(FLAGS.test_image,
- key_name=TEST_KEY,
- security_groups=[TEST_GROUP],
- user_data=user_data,
- instance_type='m1.tiny')
-
- self.data['instance'] = reservation.instances[0]
- if not self.wait_for_running(self.data['instance']):
- self.fail('instance failed to start')
- self.data['instance'].update()
-
- def test_003_can_authorize_security_group_ingress(self):
- self.assertTrue(self.conn.authorize_security_group(TEST_GROUP,
- ip_protocol='tcp',
- from_port=80,
- to_port=80))
-
- def test_004_can_access_metadata_over_public_ip(self):
- result = self.conn.allocate_address()
- self.assertTrue(hasattr(result, 'public_ip'))
- self.data['public_ip'] = result.public_ip
-
- result = self.conn.associate_address(self.data['instance'].id,
- self.data['public_ip'])
- start_time = time.time()
- try:
- while not self.__public_instance_is_accessible():
- # 1 minute to launch
- if time.time() - start_time > 60:
- raise Exception("Timeout")
- time.sleep(1)
- finally:
- result = self.conn.disassociate_address(self.data['public_ip'])
-
- def test_005_can_revoke_security_group_ingress(self):
- self.assertTrue(self.conn.revoke_security_group(TEST_GROUP,
- ip_protocol='tcp',
- from_port=80,
- to_port=80))
- start_time = time.time()
- while self.__public_instance_is_accessible():
- # 1 minute to teardown
- if time.time() - start_time > 60:
- raise Exception("Timeout")
- time.sleep(1)
-
- def test_999_tearDown(self):
- self.conn.delete_key_pair(TEST_KEY)
- self.conn.delete_security_group(TEST_GROUP)
- groups = self.conn.get_all_security_groups()
- self.assertFalse(TEST_GROUP in [group.name for group in groups])
- self.conn.terminate_instances([self.data['instance'].id])
- self.assertTrue(self.conn.release_address(self.data['public_ip']))
-
-
-if __name__ == "__main__":
- suites = {'address': unittest.makeSuite(AddressTests),
- 'security_group': unittest.makeSuite(SecurityGroupTests)
- }
- sys.exit(base.run_tests(suites))
diff --git a/smoketests/public_network_smoketests.py b/smoketests/public_network_smoketests.py
index 5a4c67642..aa1fc548f 100644
--- a/smoketests/public_network_smoketests.py
+++ b/smoketests/public_network_smoketests.py
@@ -19,7 +19,6 @@
import commands
import os
import random
-import socket
import sys
import time
import unittest
@@ -181,7 +180,3 @@ class InstanceTestsFromPublic(base.UserSmokeTestCase):
self.conn.delete_security_group(security_group_name)
if 'instance_id' in self.data:
self.conn.terminate_instances([self.data['instance_id']])
-
-if __name__ == "__main__":
- suites = {'instance': unittest.makeSuite(InstanceTestsFromPublic)}
- sys.exit(base.run_tests(suites))
diff --git a/smoketests/run_tests.py b/smoketests/run_tests.py
new file mode 100644
index 000000000..4f06f0f2b
--- /dev/null
+++ b/smoketests/run_tests.py
@@ -0,0 +1,297 @@
+#!/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.
+
+# Colorizer Code is borrowed from Twisted:
+# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+"""Unittest runner for Nova.
+
+To run all tests
+ python run_tests.py
+
+To run a single test:
+ python run_tests.py test_compute:ComputeTestCase.test_run_terminate
+
+To run a single test module:
+ python run_tests.py test_compute
+
+ or
+
+ python run_tests.py api.test_wsgi
+
+"""
+
+import gettext
+import os
+import unittest
+import sys
+
+gettext.install('nova', unicode=1)
+
+from nose import config
+from nose import core
+from nose import result
+
+
+class _AnsiColorizer(object):
+ """
+ A colorizer is an object that loosely wraps around a stream, allowing
+ callers to write text to the stream in a particular color.
+
+ Colorizer classes must implement C{supported()} and C{write(text, color)}.
+ """
+ _colors = dict(black=30, red=31, green=32, yellow=33,
+ blue=34, magenta=35, cyan=36, white=37)
+
+ def __init__(self, stream):
+ self.stream = stream
+
+ def supported(cls, stream=sys.stdout):
+ """
+ A class method that returns True if the current platform supports
+ coloring terminal output using this method. Returns False otherwise.
+ """
+ if not stream.isatty():
+ return False # auto color only on TTYs
+ try:
+ import curses
+ except ImportError:
+ return False
+ else:
+ try:
+ try:
+ return curses.tigetnum("colors") > 2
+ except curses.error:
+ curses.setupterm()
+ return curses.tigetnum("colors") > 2
+ except:
+ raise
+ # guess false in case of error
+ return False
+ supported = classmethod(supported)
+
+ def write(self, text, color):
+ """
+ Write the given text to the stream in the given color.
+
+ @param text: Text to be written to the stream.
+
+ @param color: A string label for a color. e.g. 'red', 'white'.
+ """
+ color = self._colors[color]
+ self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
+
+
+class _Win32Colorizer(object):
+ """
+ See _AnsiColorizer docstring.
+ """
+ def __init__(self, stream):
+ from win32console import GetStdHandle, STD_OUT_HANDLE, \
+ FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
+ FOREGROUND_INTENSITY
+ red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
+ FOREGROUND_BLUE, FOREGROUND_INTENSITY)
+ self.stream = stream
+ self.screenBuffer = GetStdHandle(STD_OUT_HANDLE)
+ self._colors = {
+ 'normal': red | green | blue,
+ 'red': red | bold,
+ 'green': green | bold,
+ 'blue': blue | bold,
+ 'yellow': red | green | bold,
+ 'magenta': red | blue | bold,
+ 'cyan': green | blue | bold,
+ 'white': red | green | blue | bold
+ }
+
+ def supported(cls, stream=sys.stdout):
+ try:
+ import win32console
+ screenBuffer = win32console.GetStdHandle(
+ win32console.STD_OUT_HANDLE)
+ except ImportError:
+ return False
+ import pywintypes
+ try:
+ screenBuffer.SetConsoleTextAttribute(
+ win32console.FOREGROUND_RED |
+ win32console.FOREGROUND_GREEN |
+ win32console.FOREGROUND_BLUE)
+ except pywintypes.error:
+ return False
+ else:
+ return True
+ supported = classmethod(supported)
+
+ def write(self, text, color):
+ color = self._colors[color]
+ self.screenBuffer.SetConsoleTextAttribute(color)
+ self.stream.write(text)
+ self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
+
+
+class _NullColorizer(object):
+ """
+ See _AnsiColorizer docstring.
+ """
+ def __init__(self, stream):
+ self.stream = stream
+
+ def supported(cls, stream=sys.stdout):
+ return True
+ supported = classmethod(supported)
+
+ def write(self, text, color):
+ self.stream.write(text)
+
+
+class NovaTestResult(result.TextTestResult):
+ def __init__(self, *args, **kw):
+ result.TextTestResult.__init__(self, *args, **kw)
+ self._last_case = None
+ self.colorizer = None
+ # NOTE(vish): reset stdout for the terminal check
+ stdout = sys.stdout
+ sys.stdout = sys.__stdout__
+ for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
+ if colorizer.supported():
+ self.colorizer = colorizer(self.stream)
+ break
+ sys.stdout = stdout
+
+ def getDescription(self, test):
+ return str(test)
+
+ # NOTE(vish): copied from unittest with edit to add color
+ def addSuccess(self, test):
+ unittest.TestResult.addSuccess(self, test)
+ if self.showAll:
+ self.colorizer.write("OK", 'green')
+ self.stream.writeln()
+ elif self.dots:
+ self.stream.write('.')
+ self.stream.flush()
+
+ # NOTE(vish): copied from unittest with edit to add color
+ def addFailure(self, test, err):
+ unittest.TestResult.addFailure(self, test, err)
+ if self.showAll:
+ self.colorizer.write("FAIL", 'red')
+ self.stream.writeln()
+ elif self.dots:
+ self.stream.write('F')
+ self.stream.flush()
+
+ # NOTE(vish): copied from nose with edit to add color
+ def addError(self, test, err):
+ """Overrides normal addError to add support for
+ errorClasses. If the exception is a registered class, the
+ error will be added to the list for that class, not errors.
+ """
+ stream = getattr(self, 'stream', None)
+ ec, ev, tb = err
+ try:
+ exc_info = self._exc_info_to_string(err, test)
+ except TypeError:
+ # 2.3 compat
+ exc_info = self._exc_info_to_string(err)
+ for cls, (storage, label, isfail) in self.errorClasses.items():
+ if result.isclass(ec) and issubclass(ec, cls):
+ if isfail:
+ test.passed = False
+ storage.append((test, exc_info))
+ # Might get patched into a streamless result
+ if stream is not None:
+ if self.showAll:
+ message = [label]
+ detail = result._exception_detail(err[1])
+ if detail:
+ message.append(detail)
+ stream.writeln(": ".join(message))
+ elif self.dots:
+ stream.write(label[:1])
+ return
+ self.errors.append((test, exc_info))
+ test.passed = False
+ if stream is not None:
+ if self.showAll:
+ self.colorizer.write("ERROR", 'red')
+ self.stream.writeln()
+ elif self.dots:
+ stream.write('E')
+
+ 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__':
+ if not os.getenv('EC2_ACCESS_KEY'):
+ print _('Missing EC2 environment variables. Please ' \
+ 'source the appropriate novarc file before ' \
+ 'running this test.')
+ sys.exit(1)
+
+ testdir = os.path.abspath("./")
+ c = config.Config(stream=sys.stdout,
+ env=os.environ,
+ verbosity=3,
+ workingDir=testdir,
+ plugins=core.DefaultPluginManager())
+
+ runner = NovaTestRunner(stream=c.stream,
+ verbosity=c.verbosity,
+ config=c)
+ sys.exit(not core.run(config=c, testRunner=runner, argv=sys.argv))
diff --git a/smoketests/sysadmin_smoketests.py b/smoketests/sysadmin_smoketests.py
deleted file mode 100644
index 3b267bc65..000000000
--- a/smoketests/sysadmin_smoketests.py
+++ /dev/null
@@ -1,295 +0,0 @@
-# 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 commands
-import os
-import random
-import sys
-import time
-import unittest
-
-# 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)
-
-from smoketests import flags
-from smoketests import base
-
-
-
-FLAGS = flags.FLAGS
-flags.DEFINE_string('bundle_kernel', 'openwrt-x86-vmlinuz',
- 'Local kernel file to use for bundling tests')
-flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image',
- 'Local image file to use for bundling tests')
-
-TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
-TEST_BUCKET = '%s_bucket' % TEST_PREFIX
-TEST_KEY = '%s_key' % TEST_PREFIX
-TEST_GROUP = '%s_group' % TEST_PREFIX
-class ImageTests(base.UserSmokeTestCase):
- def test_001_can_bundle_image(self):
- self.assertTrue(self.bundle_image(FLAGS.bundle_image))
-
- def test_002_can_upload_image(self):
- self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_image))
-
- def test_003_can_register_image(self):
- image_id = self.conn.register_image('%s/%s.manifest.xml' %
- (TEST_BUCKET, FLAGS.bundle_image))
- self.assert_(image_id is not None)
- self.data['image_id'] = image_id
-
- def test_004_can_bundle_kernel(self):
- self.assertTrue(self.bundle_image(FLAGS.bundle_kernel, kernel=True))
-
- def test_005_can_upload_kernel(self):
- self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_kernel))
-
- def test_006_can_register_kernel(self):
- kernel_id = self.conn.register_image('%s/%s.manifest.xml' %
- (TEST_BUCKET, FLAGS.bundle_kernel))
- self.assert_(kernel_id is not None)
- self.data['kernel_id'] = kernel_id
-
- def test_007_images_are_available_within_10_seconds(self):
- for i in xrange(10):
- image = self.conn.get_image(self.data['image_id'])
- if image and image.state == 'available':
- break
- time.sleep(1)
- else:
- self.assert_(False) # wasn't available within 10 seconds
- self.assert_(image.type == 'machine')
-
- for i in xrange(10):
- kernel = self.conn.get_image(self.data['kernel_id'])
- if kernel and kernel.state == 'available':
- break
- time.sleep(1)
- else:
- self.assert_(False) # wasn't available within 10 seconds
- self.assert_(kernel.type == 'kernel')
-
- def test_008_can_describe_image_attribute(self):
- attrs = self.conn.get_image_attribute(self.data['image_id'],
- 'launchPermission')
- self.assert_(attrs.name, 'launch_permission')
-
- def test_009_can_modify_image_launch_permission(self):
- self.conn.modify_image_attribute(image_id=self.data['image_id'],
- operation='add',
- attribute='launchPermission',
- groups='all')
- image = self.conn.get_image(self.data['image_id'])
- self.assertEqual(image.id, self.data['image_id'])
-
- def test_010_can_see_launch_permission(self):
- attrs = self.conn.get_image_attribute(self.data['image_id'],
- 'launchPermission')
- self.assert_(attrs.name, 'launch_permission')
- self.assert_(attrs.attrs['groups'][0], 'all')
-
- def test_011_user_can_deregister_kernel(self):
- self.assertTrue(self.conn.deregister_image(self.data['kernel_id']))
-
- def test_012_can_deregister_image(self):
- self.assertTrue(self.conn.deregister_image(self.data['image_id']))
-
- def test_013_can_delete_bundle(self):
- self.assertTrue(self.delete_bundle_bucket(TEST_BUCKET))
-
-
-class InstanceTests(base.UserSmokeTestCase):
- def test_001_can_create_keypair(self):
- key = self.create_key_pair(self.conn, TEST_KEY)
- self.assertEqual(key.name, TEST_KEY)
-
- def test_002_can_create_instance_with_keypair(self):
- reservation = self.conn.run_instances(FLAGS.test_image,
- key_name=TEST_KEY,
- instance_type='m1.tiny')
- self.assertEqual(len(reservation.instances), 1)
- self.data['instance'] = reservation.instances[0]
-
- def test_003_instance_runs_within_60_seconds(self):
- instance = self.data['instance']
- # allow 60 seconds to exit pending with IP
- if not self.wait_for_running(self.data['instance']):
- self.fail('instance failed to start')
- self.data['instance'].update()
- ip = self.data['instance'].private_dns_name
- self.failIf(ip == '0.0.0.0')
- if FLAGS.use_ipv6:
- ipv6 = self.data['instance'].dns_name_v6
- self.failIf(ipv6 is None)
-
- def test_004_can_ping_private_ip(self):
- if not self.wait_for_ping(self.data['instance'].private_dns_name):
- self.fail('could not ping instance')
-
- if FLAGS.use_ipv6:
- if not self.wait_for_ping(self.data['instance'].ip_v6, "ping6"):
- self.fail('could not ping instance v6')
-
- def test_005_can_ssh_to_private_ip(self):
- if not self.wait_for_ssh(self.data['instance'].private_dns_name,
- TEST_KEY):
- self.fail('could not ssh to instance')
-
- if FLAGS.use_ipv6:
- if not self.wait_for_ssh(self.data['instance'].ip_v6,
- TEST_KEY):
- self.fail('could not ssh to instance v6')
-
- def test_999_tearDown(self):
- self.delete_key_pair(self.conn, TEST_KEY)
- self.conn.terminate_instances([self.data['instance'].id])
-
-
-class VolumeTests(base.UserSmokeTestCase):
- def setUp(self):
- super(VolumeTests, self).setUp()
- self.device = '/dev/vdb'
-
- def test_000_setUp(self):
- self.create_key_pair(self.conn, TEST_KEY)
- reservation = self.conn.run_instances(FLAGS.test_image,
- instance_type='m1.tiny',
- key_name=TEST_KEY)
- self.data['instance'] = reservation.instances[0]
- if not self.wait_for_running(self.data['instance']):
- self.fail('instance failed to start')
- self.data['instance'].update()
- if not self.wait_for_ping(self.data['instance'].private_dns_name):
- self.fail('could not ping instance')
- if not self.wait_for_ssh(self.data['instance'].private_dns_name,
- TEST_KEY):
- self.fail('could not ssh to instance')
-
- def test_001_can_create_volume(self):
- volume = self.conn.create_volume(1, 'nova')
- self.assertEqual(volume.size, 1)
- self.data['volume'] = volume
- # Give network time to find volume.
- time.sleep(5)
-
- def test_002_can_attach_volume(self):
- volume = self.data['volume']
-
- for x in xrange(10):
- volume.update()
- if volume.status.startswith('available'):
- break
- time.sleep(1)
- else:
- self.fail('cannot attach volume with state %s' % volume.status)
-
- # Give volume some time to be ready.
- time.sleep(5)
- volume.attach(self.data['instance'].id, self.device)
-
- # wait
- for x in xrange(10):
- volume.update()
- if volume.status.startswith('in-use'):
- break
- time.sleep(1)
- else:
- self.fail('volume never got to in use')
-
- self.assertTrue(volume.status.startswith('in-use'))
-
- # Give instance time to recognize volume.
- time.sleep(5)
-
- def test_003_can_mount_volume(self):
- ip = self.data['instance'].private_dns_name
- conn = self.connect_ssh(ip, TEST_KEY)
- # NOTE(vish): this will create an dev for images that don't have
- # udev rules
- stdin, stdout, stderr = conn.exec_command(
- 'grep %s /proc/partitions | '
- '`awk \'{print "mknod /dev/"\\$4" b "\\$1" "\\$2}\'`'
- % self.device.rpartition('/')[2])
- exec_list = []
- exec_list.append('mkdir -p /mnt/vol')
- exec_list.append('/sbin/mke2fs %s' % self.device)
- exec_list.append('mount %s /mnt/vol' % self.device)
- exec_list.append('echo success')
- stdin, stdout, stderr = conn.exec_command(' && '.join(exec_list))
- out = stdout.read()
- conn.close()
- if not out.strip().endswith('success'):
- self.fail('Unable to mount: %s %s' % (out, stderr.read()))
-
- def test_004_can_write_to_volume(self):
- ip = self.data['instance'].private_dns_name
- conn = self.connect_ssh(ip, TEST_KEY)
- # FIXME(devcamcar): This doesn't fail if the volume hasn't been mounted
- stdin, stdout, stderr = conn.exec_command(
- 'echo hello > /mnt/vol/test.txt')
- err = stderr.read()
- conn.close()
- if len(err) > 0:
- self.fail('Unable to write to mount: %s' % (err))
-
- def test_005_volume_is_correct_size(self):
- ip = self.data['instance'].private_dns_name
- conn = self.connect_ssh(ip, TEST_KEY)
- stdin, stdout, stderr = conn.exec_command(
- "df -h | grep %s | awk {'print $2'}" % self.device)
- out = stdout.read()
- conn.close()
- if not out.strip() == '1007.9M':
- self.fail('Volume is not the right size: %s %s' %
- (out, stderr.read()))
-
- def test_006_me_can_umount_volume(self):
- ip = self.data['instance'].private_dns_name
- conn = self.connect_ssh(ip, TEST_KEY)
- stdin, stdout, stderr = conn.exec_command('umount /mnt/vol')
- err = stderr.read()
- conn.close()
- if len(err) > 0:
- self.fail('Unable to unmount: %s' % (err))
-
- def test_007_me_can_detach_volume(self):
- result = self.conn.detach_volume(volume_id=self.data['volume'].id)
- self.assertTrue(result)
- time.sleep(5)
-
- def test_008_me_can_delete_volume(self):
- result = self.conn.delete_volume(self.data['volume'].id)
- self.assertTrue(result)
-
- def test_999_tearDown(self):
- self.conn.terminate_instances([self.data['instance'].id])
- self.conn.delete_key_pair(TEST_KEY)
-
-
-if __name__ == "__main__":
- suites = {'image': unittest.makeSuite(ImageTests),
- 'instance': unittest.makeSuite(InstanceTests),
- 'volume': unittest.makeSuite(VolumeTests)
- }
- sys.exit(base.run_tests(suites))
diff --git a/smoketests/test_admin.py b/smoketests/test_admin.py
new file mode 100644
index 000000000..46e5b2233
--- /dev/null
+++ b/smoketests/test_admin.py
@@ -0,0 +1,91 @@
+# 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 random
+import sys
+import unittest
+import zipfile
+
+# 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)
+
+from nova import adminclient
+from smoketests import flags
+from smoketests import base
+
+
+FLAGS = flags.FLAGS
+
+# TODO(devamcar): Use random tempfile
+ZIP_FILENAME = '/tmp/nova-me-x509.zip'
+
+TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
+TEST_USERNAME = '%suser' % TEST_PREFIX
+TEST_PROJECTNAME = '%sproject' % TEST_PREFIX
+
+
+class AdminSmokeTestCase(base.SmokeTestCase):
+ def setUp(self):
+ self.admin = adminclient.NovaAdminClient(
+ access_key=os.getenv('EC2_ACCESS_KEY'),
+ secret_key=os.getenv('EC2_SECRET_KEY'),
+ clc_url=os.getenv('EC2_URL'),
+ region=FLAGS.region)
+
+
+class UserTests(AdminSmokeTestCase):
+ """ Test admin credentials and user creation. """
+
+ def test_001_admin_can_connect(self):
+ conn = self.admin.connection_for('admin', 'admin')
+ self.assert_(conn)
+
+ def test_002_admin_can_create_user(self):
+ user = self.admin.create_user(TEST_USERNAME)
+ self.assertEqual(user.username, TEST_USERNAME)
+
+ def test_003_admin_can_create_project(self):
+ project = self.admin.create_project(TEST_PROJECTNAME,
+ TEST_USERNAME)
+ self.assertEqual(project.projectname, TEST_PROJECTNAME)
+
+ def test_004_user_can_download_credentials(self):
+ buf = self.admin.get_zip(TEST_USERNAME, TEST_PROJECTNAME)
+ output = open(ZIP_FILENAME, 'w')
+ output.write(buf)
+ output.close()
+
+ zip = zipfile.ZipFile(ZIP_FILENAME, 'a', zipfile.ZIP_DEFLATED)
+ bad = zip.testzip()
+ zip.close()
+
+ self.failIf(bad)
+
+ def test_999_tearDown(self):
+ self.admin.delete_project(TEST_PROJECTNAME)
+ self.admin.delete_user(TEST_USERNAME)
+ try:
+ os.remove(ZIP_FILENAME)
+ except:
+ pass
diff --git a/smoketests/test_netadmin.py b/smoketests/test_netadmin.py
new file mode 100644
index 000000000..4f033e4bc
--- /dev/null
+++ b/smoketests/test_netadmin.py
@@ -0,0 +1,184 @@
+# 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 commands
+import os
+import random
+import sys
+import time
+import unittest
+
+# 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)
+
+from smoketests import flags
+from smoketests import base
+
+
+FLAGS = flags.FLAGS
+
+TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
+TEST_BUCKET = '%s_bucket' % TEST_PREFIX
+TEST_KEY = '%s_key' % TEST_PREFIX
+TEST_GROUP = '%s_group' % TEST_PREFIX
+
+
+class AddressTests(base.UserSmokeTestCase):
+ def test_000_setUp(self):
+ self.create_key_pair(self.conn, TEST_KEY)
+ reservation = self.conn.run_instances(FLAGS.test_image,
+ instance_type='m1.tiny',
+ key_name=TEST_KEY)
+ self.data['instance'] = reservation.instances[0]
+ if not self.wait_for_running(self.data['instance']):
+ self.fail('instance failed to start')
+ self.data['instance'].update()
+ if not self.wait_for_ping(self.data['instance'].private_dns_name):
+ self.fail('could not ping instance')
+ if not self.wait_for_ssh(self.data['instance'].private_dns_name,
+ TEST_KEY):
+ self.fail('could not ssh to instance')
+
+ def test_001_can_allocate_floating_ip(self):
+ result = self.conn.allocate_address()
+ self.assertTrue(hasattr(result, 'public_ip'))
+ self.data['public_ip'] = result.public_ip
+
+ def test_002_can_associate_ip_with_instance(self):
+ result = self.conn.associate_address(self.data['instance'].id,
+ self.data['public_ip'])
+ self.assertTrue(result)
+
+ def test_003_can_ssh_with_public_ip(self):
+ ssh_authorized = False
+ groups = self.conn.get_all_security_groups(['default'])
+ for rule in groups[0].rules:
+ if (rule.ip_protocol == 'tcp' and
+ int(rule.from_port) <= 22 and
+ int(rule.to_port) >= 22):
+ ssh_authorized = True
+ break
+ if not ssh_authorized:
+ self.conn.authorize_security_group('default',
+ ip_protocol='tcp',
+ from_port=22,
+ to_port=22)
+ try:
+ if not self.wait_for_ssh(self.data['public_ip'], TEST_KEY):
+ self.fail('could not ssh to public ip')
+ finally:
+ if not ssh_authorized:
+ self.conn.revoke_security_group('default',
+ ip_protocol='tcp',
+ from_port=22,
+ to_port=22)
+
+ def test_004_can_disassociate_ip_from_instance(self):
+ result = self.conn.disassociate_address(self.data['public_ip'])
+ self.assertTrue(result)
+
+ def test_005_can_deallocate_floating_ip(self):
+ result = self.conn.release_address(self.data['public_ip'])
+ self.assertTrue(result)
+
+ def test_999_tearDown(self):
+ self.delete_key_pair(self.conn, TEST_KEY)
+ self.conn.terminate_instances([self.data['instance'].id])
+
+
+class SecurityGroupTests(base.UserSmokeTestCase):
+
+ def __public_instance_is_accessible(self):
+ id_url = "latest/meta-data/instance-id"
+ options = "-s --max-time 1"
+ command = "curl %s %s/%s" % (options, self.data['public_ip'], id_url)
+ instance_id = commands.getoutput(command).strip()
+ if not instance_id:
+ return False
+ if instance_id != self.data['instance'].id:
+ raise Exception("Wrong instance id")
+ return True
+
+ def test_001_can_create_security_group(self):
+ self.conn.create_security_group(TEST_GROUP, description='test')
+
+ groups = self.conn.get_all_security_groups()
+ self.assertTrue(TEST_GROUP in [group.name for group in groups])
+
+ def test_002_can_launch_instance_in_security_group(self):
+ with open("proxy.sh") as f:
+ user_data = f.read()
+ self.create_key_pair(self.conn, TEST_KEY)
+ reservation = self.conn.run_instances(FLAGS.test_image,
+ key_name=TEST_KEY,
+ security_groups=[TEST_GROUP],
+ user_data=user_data,
+ instance_type='m1.tiny')
+
+ self.data['instance'] = reservation.instances[0]
+ if not self.wait_for_running(self.data['instance']):
+ self.fail('instance failed to start')
+ self.data['instance'].update()
+
+ def test_003_can_authorize_security_group_ingress(self):
+ self.assertTrue(self.conn.authorize_security_group(TEST_GROUP,
+ ip_protocol='tcp',
+ from_port=80,
+ to_port=80))
+
+ def test_004_can_access_metadata_over_public_ip(self):
+ result = self.conn.allocate_address()
+ self.assertTrue(hasattr(result, 'public_ip'))
+ self.data['public_ip'] = result.public_ip
+
+ result = self.conn.associate_address(self.data['instance'].id,
+ self.data['public_ip'])
+ start_time = time.time()
+ try:
+ while not self.__public_instance_is_accessible():
+ # 1 minute to launch
+ if time.time() - start_time > 60:
+ raise Exception("Timeout")
+ time.sleep(1)
+ finally:
+ result = self.conn.disassociate_address(self.data['public_ip'])
+
+ def test_005_can_revoke_security_group_ingress(self):
+ self.assertTrue(self.conn.revoke_security_group(TEST_GROUP,
+ ip_protocol='tcp',
+ from_port=80,
+ to_port=80))
+ start_time = time.time()
+ while self.__public_instance_is_accessible():
+ # 1 minute to teardown
+ if time.time() - start_time > 60:
+ raise Exception("Timeout")
+ time.sleep(1)
+
+ def test_999_tearDown(self):
+ self.conn.delete_key_pair(TEST_KEY)
+ self.conn.delete_security_group(TEST_GROUP)
+ groups = self.conn.get_all_security_groups()
+ self.assertFalse(TEST_GROUP in [group.name for group in groups])
+ self.conn.terminate_instances([self.data['instance'].id])
+ self.assertTrue(self.conn.release_address(self.data['public_ip']))
diff --git a/smoketests/test_sysadmin.py b/smoketests/test_sysadmin.py
new file mode 100644
index 000000000..4950b07e7
--- /dev/null
+++ b/smoketests/test_sysadmin.py
@@ -0,0 +1,287 @@
+# 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 commands
+import os
+import random
+import sys
+import time
+import unittest
+
+# 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)
+
+from smoketests import flags
+from smoketests import base
+
+
+
+FLAGS = flags.FLAGS
+flags.DEFINE_string('bundle_kernel', 'openwrt-x86-vmlinuz',
+ 'Local kernel file to use for bundling tests')
+flags.DEFINE_string('bundle_image', 'openwrt-x86-ext2.image',
+ 'Local image file to use for bundling tests')
+
+TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
+TEST_BUCKET = '%s_bucket' % TEST_PREFIX
+TEST_KEY = '%s_key' % TEST_PREFIX
+TEST_GROUP = '%s_group' % TEST_PREFIX
+class ImageTests(base.UserSmokeTestCase):
+ def test_001_can_bundle_image(self):
+ self.assertTrue(self.bundle_image(FLAGS.bundle_image))
+
+ def test_002_can_upload_image(self):
+ self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_image))
+
+ def test_003_can_register_image(self):
+ image_id = self.conn.register_image('%s/%s.manifest.xml' %
+ (TEST_BUCKET, FLAGS.bundle_image))
+ self.assert_(image_id is not None)
+ self.data['image_id'] = image_id
+
+ def test_004_can_bundle_kernel(self):
+ self.assertTrue(self.bundle_image(FLAGS.bundle_kernel, kernel=True))
+
+ def test_005_can_upload_kernel(self):
+ self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_kernel))
+
+ def test_006_can_register_kernel(self):
+ kernel_id = self.conn.register_image('%s/%s.manifest.xml' %
+ (TEST_BUCKET, FLAGS.bundle_kernel))
+ self.assert_(kernel_id is not None)
+ self.data['kernel_id'] = kernel_id
+
+ def test_007_images_are_available_within_10_seconds(self):
+ for i in xrange(10):
+ image = self.conn.get_image(self.data['image_id'])
+ if image and image.state == 'available':
+ break
+ time.sleep(1)
+ else:
+ self.assert_(False) # wasn't available within 10 seconds
+ self.assert_(image.type == 'machine')
+
+ for i in xrange(10):
+ kernel = self.conn.get_image(self.data['kernel_id'])
+ if kernel and kernel.state == 'available':
+ break
+ time.sleep(1)
+ else:
+ self.assert_(False) # wasn't available within 10 seconds
+ self.assert_(kernel.type == 'kernel')
+
+ def test_008_can_describe_image_attribute(self):
+ attrs = self.conn.get_image_attribute(self.data['image_id'],
+ 'launchPermission')
+ self.assert_(attrs.name, 'launch_permission')
+
+ def test_009_can_modify_image_launch_permission(self):
+ self.conn.modify_image_attribute(image_id=self.data['image_id'],
+ operation='add',
+ attribute='launchPermission',
+ groups='all')
+ image = self.conn.get_image(self.data['image_id'])
+ self.assertEqual(image.id, self.data['image_id'])
+
+ def test_010_can_see_launch_permission(self):
+ attrs = self.conn.get_image_attribute(self.data['image_id'],
+ 'launchPermission')
+ self.assert_(attrs.name, 'launch_permission')
+ self.assert_(attrs.attrs['groups'][0], 'all')
+
+ def test_011_user_can_deregister_kernel(self):
+ self.assertTrue(self.conn.deregister_image(self.data['kernel_id']))
+
+ def test_012_can_deregister_image(self):
+ self.assertTrue(self.conn.deregister_image(self.data['image_id']))
+
+ def test_013_can_delete_bundle(self):
+ self.assertTrue(self.delete_bundle_bucket(TEST_BUCKET))
+
+
+class InstanceTests(base.UserSmokeTestCase):
+ def test_001_can_create_keypair(self):
+ key = self.create_key_pair(self.conn, TEST_KEY)
+ self.assertEqual(key.name, TEST_KEY)
+
+ def test_002_can_create_instance_with_keypair(self):
+ reservation = self.conn.run_instances(FLAGS.test_image,
+ key_name=TEST_KEY,
+ instance_type='m1.tiny')
+ self.assertEqual(len(reservation.instances), 1)
+ self.data['instance'] = reservation.instances[0]
+
+ def test_003_instance_runs_within_60_seconds(self):
+ instance = self.data['instance']
+ # allow 60 seconds to exit pending with IP
+ if not self.wait_for_running(self.data['instance']):
+ self.fail('instance failed to start')
+ self.data['instance'].update()
+ ip = self.data['instance'].private_dns_name
+ self.failIf(ip == '0.0.0.0')
+ if FLAGS.use_ipv6:
+ ipv6 = self.data['instance'].dns_name_v6
+ self.failIf(ipv6 is None)
+
+ def test_004_can_ping_private_ip(self):
+ if not self.wait_for_ping(self.data['instance'].private_dns_name):
+ self.fail('could not ping instance')
+
+ if FLAGS.use_ipv6:
+ if not self.wait_for_ping(self.data['instance'].ip_v6, "ping6"):
+ self.fail('could not ping instance v6')
+
+ def test_005_can_ssh_to_private_ip(self):
+ if not self.wait_for_ssh(self.data['instance'].private_dns_name,
+ TEST_KEY):
+ self.fail('could not ssh to instance')
+
+ if FLAGS.use_ipv6:
+ if not self.wait_for_ssh(self.data['instance'].ip_v6,
+ TEST_KEY):
+ self.fail('could not ssh to instance v6')
+
+ def test_999_tearDown(self):
+ self.delete_key_pair(self.conn, TEST_KEY)
+ self.conn.terminate_instances([self.data['instance'].id])
+
+
+class VolumeTests(base.UserSmokeTestCase):
+ def setUp(self):
+ super(VolumeTests, self).setUp()
+ self.device = '/dev/vdb'
+
+ def test_000_setUp(self):
+ self.create_key_pair(self.conn, TEST_KEY)
+ reservation = self.conn.run_instances(FLAGS.test_image,
+ instance_type='m1.tiny',
+ key_name=TEST_KEY)
+ self.data['instance'] = reservation.instances[0]
+ if not self.wait_for_running(self.data['instance']):
+ self.fail('instance failed to start')
+ self.data['instance'].update()
+ if not self.wait_for_ping(self.data['instance'].private_dns_name):
+ self.fail('could not ping instance')
+ if not self.wait_for_ssh(self.data['instance'].private_dns_name,
+ TEST_KEY):
+ self.fail('could not ssh to instance')
+
+ def test_001_can_create_volume(self):
+ volume = self.conn.create_volume(1, 'nova')
+ self.assertEqual(volume.size, 1)
+ self.data['volume'] = volume
+ # Give network time to find volume.
+ time.sleep(5)
+
+ def test_002_can_attach_volume(self):
+ volume = self.data['volume']
+
+ for x in xrange(10):
+ volume.update()
+ if volume.status.startswith('available'):
+ break
+ time.sleep(1)
+ else:
+ self.fail('cannot attach volume with state %s' % volume.status)
+
+ # Give volume some time to be ready.
+ time.sleep(5)
+ volume.attach(self.data['instance'].id, self.device)
+
+ # wait
+ for x in xrange(10):
+ volume.update()
+ if volume.status.startswith('in-use'):
+ break
+ time.sleep(1)
+ else:
+ self.fail('volume never got to in use')
+
+ self.assertTrue(volume.status.startswith('in-use'))
+
+ # Give instance time to recognize volume.
+ time.sleep(5)
+
+ def test_003_can_mount_volume(self):
+ ip = self.data['instance'].private_dns_name
+ conn = self.connect_ssh(ip, TEST_KEY)
+ # NOTE(vish): this will create an dev for images that don't have
+ # udev rules
+ stdin, stdout, stderr = conn.exec_command(
+ 'grep %s /proc/partitions | '
+ '`awk \'{print "mknod /dev/"\\$4" b "\\$1" "\\$2}\'`'
+ % self.device.rpartition('/')[2])
+ exec_list = []
+ exec_list.append('mkdir -p /mnt/vol')
+ exec_list.append('/sbin/mke2fs %s' % self.device)
+ exec_list.append('mount %s /mnt/vol' % self.device)
+ exec_list.append('echo success')
+ stdin, stdout, stderr = conn.exec_command(' && '.join(exec_list))
+ out = stdout.read()
+ conn.close()
+ if not out.strip().endswith('success'):
+ self.fail('Unable to mount: %s %s' % (out, stderr.read()))
+
+ def test_004_can_write_to_volume(self):
+ ip = self.data['instance'].private_dns_name
+ conn = self.connect_ssh(ip, TEST_KEY)
+ # FIXME(devcamcar): This doesn't fail if the volume hasn't been mounted
+ stdin, stdout, stderr = conn.exec_command(
+ 'echo hello > /mnt/vol/test.txt')
+ err = stderr.read()
+ conn.close()
+ if len(err) > 0:
+ self.fail('Unable to write to mount: %s' % (err))
+
+ def test_005_volume_is_correct_size(self):
+ ip = self.data['instance'].private_dns_name
+ conn = self.connect_ssh(ip, TEST_KEY)
+ stdin, stdout, stderr = conn.exec_command(
+ "df -h | grep %s | awk {'print $2'}" % self.device)
+ out = stdout.read()
+ conn.close()
+ if not out.strip() == '1007.9M':
+ self.fail('Volume is not the right size: %s %s' %
+ (out, stderr.read()))
+
+ def test_006_me_can_umount_volume(self):
+ ip = self.data['instance'].private_dns_name
+ conn = self.connect_ssh(ip, TEST_KEY)
+ stdin, stdout, stderr = conn.exec_command('umount /mnt/vol')
+ err = stderr.read()
+ conn.close()
+ if len(err) > 0:
+ self.fail('Unable to unmount: %s' % (err))
+
+ def test_007_me_can_detach_volume(self):
+ result = self.conn.detach_volume(volume_id=self.data['volume'].id)
+ self.assertTrue(result)
+ time.sleep(5)
+
+ def test_008_me_can_delete_volume(self):
+ result = self.conn.delete_volume(self.data['volume'].id)
+ self.assertTrue(result)
+
+ def test_999_tearDown(self):
+ self.conn.terminate_instances([self.data['instance'].id])
+ self.conn.delete_key_pair(TEST_KEY)
--
cgit
From a6f607681f8eac043bfbc33c91436198f451d9e1 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Thu, 24 Feb 2011 21:32:27 -0800
Subject: add customizable tempdir and remove extra code
---
smoketests/base.py | 38 +++++++-------------------------------
smoketests/test_sysadmin.py | 16 ++++++++++++----
2 files changed, 19 insertions(+), 35 deletions(-)
diff --git a/smoketests/base.py b/smoketests/base.py
index 204b4a1eb..bc9aebf6b 100644
--- a/smoketests/base.py
+++ b/smoketests/base.py
@@ -22,6 +22,7 @@ import httplib
import os
import paramiko
import sys
+import tempfile
import time
import unittest
from boto.ec2.regioninfo import RegionInfo
@@ -147,19 +148,20 @@ class SmokeTestCase(unittest.TestCase):
except:
pass
- def bundle_image(self, image, kernel=False):
- cmd = 'euca-bundle-image -i %s' % image
+ def bundle_image(self, image, tempdir='/tmp', kernel=False):
+ tempdir = tempfile.mkdtemp()
+ cmd = 'euca-bundle-image -i %s -d %s' % (image, tempdir)
if kernel:
cmd += ' --kernel true'
status, output = commands.getstatusoutput(cmd)
if status != 0:
print '%s -> \n %s' % (cmd, output)
raise Exception(output)
- return True
+ return tempdir
- def upload_image(self, bucket_name, image):
+ def upload_image(self, bucket_name, image, tempdir='/tmp'):
cmd = 'euca-upload-bundle -b '
- cmd += '%s -m /tmp/%s.manifest.xml' % (bucket_name, image)
+ cmd += '%s -m %s/%s.manifest.xml' % (bucket_name, tempdir, image)
status, output = commands.getstatusoutput(cmd)
if status != 0:
print '%s -> \n %s' % (cmd, output)
@@ -183,29 +185,3 @@ class UserSmokeTestCase(SmokeTestCase):
global TEST_DATA
self.conn = self.connection_for_env()
self.data = TEST_DATA
-
-
-def run_tests(suites):
- argv = FLAGS(sys.argv)
- if FLAGS.use_ipv6:
- global boto_v6
- boto_v6 = __import__('boto_v6')
-
- if not os.getenv('EC2_ACCESS_KEY'):
- print >> sys.stderr, 'Missing EC2 environment variables. Please ' \
- 'source the appropriate novarc file before ' \
- 'running this test.'
- return 1
-
- if FLAGS.suite:
- try:
- suite = suites[FLAGS.suite]
- except KeyError:
- print >> sys.stderr, 'Available test suites:', \
- ', '.join(suites.keys())
- return 1
-
- unittest.TextTestRunner(verbosity=2).run(suite)
- else:
- for suite in suites.itervalues():
- unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/smoketests/test_sysadmin.py b/smoketests/test_sysadmin.py
index 4950b07e7..8decb56c1 100644
--- a/smoketests/test_sysadmin.py
+++ b/smoketests/test_sysadmin.py
@@ -16,12 +16,12 @@
# License for the specific language governing permissions and limitations
# under the License.
-import commands
import os
import random
import sys
import time
-import unittest
+import tempfile
+import shutil
# 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...
@@ -48,10 +48,18 @@ TEST_KEY = '%s_key' % TEST_PREFIX
TEST_GROUP = '%s_group' % TEST_PREFIX
class ImageTests(base.UserSmokeTestCase):
def test_001_can_bundle_image(self):
- self.assertTrue(self.bundle_image(FLAGS.bundle_image))
+ self.data['tempdir'] = tempfile.mkdtemp()
+ self.assertTrue(self.bundle_image(FLAGS.bundle_image,
+ self.data['tempdir']))
def test_002_can_upload_image(self):
- self.assertTrue(self.upload_image(TEST_BUCKET, FLAGS.bundle_image))
+ try:
+ self.assertTrue(self.upload_image(TEST_BUCKET,
+ FLAGS.bundle_image,
+ self.data['tempdir']))
+ finally:
+ if os.path.exists(self.data['tempdir']):
+ shutil.rmtree(self.data['tempdir'])
def test_003_can_register_image(self):
image_id = self.conn.register_image('%s/%s.manifest.xml' %
--
cgit
From 24eb5c0b787d2031999aff21c471b0d9220083e3 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Thu, 24 Feb 2011 21:33:26 -0800
Subject: removed unused references to unittest
---
smoketests/public_network_smoketests.py | 1 -
smoketests/test_netadmin.py | 1 -
2 files changed, 2 deletions(-)
diff --git a/smoketests/public_network_smoketests.py b/smoketests/public_network_smoketests.py
index aa1fc548f..0ba477b7c 100644
--- a/smoketests/public_network_smoketests.py
+++ b/smoketests/public_network_smoketests.py
@@ -21,7 +21,6 @@ import os
import random
import sys
import time
-import unittest
# 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...
diff --git a/smoketests/test_netadmin.py b/smoketests/test_netadmin.py
index 4f033e4bc..60086f065 100644
--- a/smoketests/test_netadmin.py
+++ b/smoketests/test_netadmin.py
@@ -21,7 +21,6 @@ import os
import random
import sys
import time
-import unittest
# 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...
--
cgit
From bc7b96f11ee2ee949182f7128db6b9ff1866a247 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Thu, 24 Feb 2011 22:02:50 -0800
Subject: revert a few unnecessary changes to base.py
---
smoketests/base.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/smoketests/base.py b/smoketests/base.py
index bc9aebf6b..e9924f0ef 100644
--- a/smoketests/base.py
+++ b/smoketests/base.py
@@ -22,7 +22,6 @@ import httplib
import os
import paramiko
import sys
-import tempfile
import time
import unittest
from boto.ec2.regioninfo import RegionInfo
@@ -149,7 +148,6 @@ class SmokeTestCase(unittest.TestCase):
pass
def bundle_image(self, image, tempdir='/tmp', kernel=False):
- tempdir = tempfile.mkdtemp()
cmd = 'euca-bundle-image -i %s -d %s' % (image, tempdir)
if kernel:
cmd += ' --kernel true'
@@ -157,7 +155,7 @@ class SmokeTestCase(unittest.TestCase):
if status != 0:
print '%s -> \n %s' % (cmd, output)
raise Exception(output)
- return tempdir
+ return True
def upload_image(self, bucket_name, image, tempdir='/tmp'):
cmd = 'euca-upload-bundle -b '
--
cgit
From 20e9df05acdb89382023af1ac98cf040c827e18d Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Fri, 25 Feb 2011 14:49:33 -0800
Subject: DescribeInstances modified to return ipv6 fixed ip address in case of
flatmanager
---
nova/api/ec2/cloud.py | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 7458d307a..15799670c 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -701,9 +701,13 @@ class CloudController(object):
fixed = instance['fixed_ip']
floating_addr = fixed['floating_ips'][0]['address']
if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
- i['dnsNameV6'] = utils.to_global_ipv6(
- instance['fixed_ip']['network']['cidr_v6'],
- instance['mac_address'])
+ if FLAGS.network_manager == \
+ 'nova.network.manager.FlatManager':
+ i['dnsNameV6'] = instance['fixed_ip']['addressv6']
+ else:
+ i['dnsNameV6'] = utils.to_global_ipv6(
+ instance['fixed_ip']['network']['cidr_v6'],
+ instance['mac_address'])
i['privateDnsName'] = fixed_addr
i['publicDnsName'] = floating_addr
--
cgit
From 5bb77cb83ea443e5e3ae4b4000763e4289f8e87a Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Fri, 25 Feb 2011 23:58:36 -0800
Subject: add timeout and retry for ssh
---
smoketests/base.py | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/smoketests/base.py b/smoketests/base.py
index e9924f0ef..3e2446c9a 100644
--- a/smoketests/base.py
+++ b/smoketests/base.py
@@ -31,17 +31,24 @@ from smoketests import flags
SUITE_NAMES = '[image, instance, volume]'
FLAGS = flags.FLAGS
flags.DEFINE_string('suite', None, 'Specific test suite to run ' + SUITE_NAMES)
+flags.DEFINE_integer('ssh_tries', 3, 'Numer of times to try ssh')
boto_v6 = None
class SmokeTestCase(unittest.TestCase):
def connect_ssh(self, ip, key_name):
- # TODO(devcamcar): set a more reasonable connection timeout time
key = paramiko.RSAKey.from_private_key_file('/tmp/%s.pem' % key_name)
- client = paramiko.SSHClient()
- client.set_missing_host_key_policy(paramiko.WarningPolicy())
- client.connect(ip, username='root', pkey=key)
- return client
+ tries = 0
+ while(True):
+ try:
+ client = paramiko.SSHClient()
+ client.set_missing_host_key_policy(paramiko.WarningPolicy())
+ client.connect(ip, username='root', pkey=key, timeout=5)
+ return client
+ except (paramiko.AuthenticationException, paramiko.SSHException):
+ tries += 1
+ if tries == FLAGS.ssh_tries:
+ raise
def can_ping(self, ip, command="ping"):
"""Attempt to ping the specified IP, and give up after 1 second."""
--
cgit
From 493aa34da71e7dc3c28c6a55254b6d7ed4d81b72 Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: Sat, 26 Feb 2011 22:13:28 +0100
Subject: beautification...
---
bin/nova-manage | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 68847bdde..2e4e091cf 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -623,8 +623,8 @@ class InstanceCommands(object):
def list(self, host=None, instance=None):
"""Show a list of all instances"""
- print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s" \
- " %-12s %-10s %-10s %-10s %-5s" % (
+ print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
+ " %-10s %-10s %-10s %-5s" % (
_('instance'),
_('node'),
_('type'),
@@ -636,7 +636,7 @@ class InstanceCommands(object):
_('project'),
_('user'),
_('zone'),
- _('index'),
+ _('index')
)
for instance in db.instance_get_all(context.get_admin_context()):
@@ -716,8 +716,8 @@ CATEGORIES = [
('service', ServiceCommands),
('log', LogCommands),
('db', DbCommands),
- ('volume', VolumeCommands)
- ('instance', InstanceCommands),
+ ('volume', VolumeCommands),
+ ('instance', InstanceCommands)
]
--
cgit
From 2714b2df0d21ecb08966c4d145d2d75fa1bb201d Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: Sun, 27 Feb 2011 00:07:03 +0100
Subject: fixed FIXME
---
nova/db/sqlalchemy/api.py | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index d07c53759..828d24c78 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -598,17 +598,11 @@ def fixed_ip_get_all(context, session=None):
def fixed_ip_get_all_by_host(context, host=None):
session = get_session()
- # FIXME: I'm sure that SQLAlchemy can handle this in a nicer way
- instances = session.query(models.Instance).\
- filter_by(state=1).\
- filter_by(host=host).\
- all()
-
- result = []
- for instance in instances:
- result.append(session.query(models.FixedIp).\
- filter_by(instance_id=instance['id']).\
- first())
+ result = session.query(models.FixedIp).\
+ join(models.FixedIp.instance).\
+ filter_by(state=1).\
+ filter_by(host=host).\
+ all()
if not result:
raise exception.NotFound(_('No fixed ips for this host defined'))
--
cgit
From f72e5b618387a7b5a06f0e5b7e68af51c6667327 Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: Sun, 27 Feb 2011 00:13:05 +0100
Subject: added listing of instances running on a specific host
chronos:~ # nova-manage fixed list ares
network IP address MAC address hostname host
192.168.3.0/24 192.168.3.6 02:16:3e:75:d7:9a i-00000c1c ares
---
bin/nova-manage | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 2e4e091cf..12ccfa0e9 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -639,7 +639,13 @@ class InstanceCommands(object):
_('index')
)
- for instance in db.instance_get_all(context.get_admin_context()):
+ if host == None:
+ instances = db.instance_get_all(context.get_admin_context())
+ else:
+ instances = db.instance_get_all_by_host(
+ context.get_admin_context(), host)
+
+ for instance in instances:
print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
" %-10s %-10s %-10s %-5d" % (
instance['hostname'],
--
cgit
From 0550124fcd863be60dd0e6fefb5f30641331b198 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Mon, 28 Feb 2011 18:06:11 -0500
Subject: add test for instance creation without personalities
---
nova/tests/api/openstack/test_servers.py | 40 ++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 7a25abe9d..7e5bc0080 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -232,6 +232,46 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
+ def _create_instance_with_personality(self, personality):
+
+ class FakeComputeAPI(object):
+
+ def __init__(self):
+ self.onset_files = None
+
+ def create(*args, **kwargs):
+ if 'onset_files' in kwargs:
+ self.onset_files = kwargs['onset_files']
+ else:
+ self.onset_files = None
+ return [{'id': '1234', 'display_name': 'fakeinstance'}]
+
+ def make_stub_method(canned_return):
+ def stub_method(*args, **kwargs):
+ return canned_return
+ return stub_method
+
+ compute_api = FakeComputeAPI()
+ self.stubs.Set(nova.compute, 'API', make_stub_method(compute_api))
+ self.stubs.Set(nova.api.openstack.servers.Controller,
+ '_get_kernel_ramdisk_from_image', make_stub_method((1, 1)))
+ self.stubs.Set(nova.api.openstack.common,
+ 'get_image_id_from_image_hash', make_stub_method(2))
+ body = dict(server=dict(
+ name='server_test', imageId=2, flavorId=2,
+ metadata={},
+ personality=personality))
+
+ req = webob.Request.blank('/v1.0/servers')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+ return req.get_response(fakes.wsgi_app()), compute_api.onset_files
+
+ def test_create_instance_with_no_personality(self):
+ res, onset_files = self._create_instance_with_personality(personality={})
+ self.assertEquals(res.status_int, 200)
+ self.assertEquals(onset_files, None)
+
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
--
cgit
From 1caa7f189827b4721c2e9d3ddf753acd749d7916 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Tue, 1 Mar 2011 17:52:46 +0900
Subject: rename db migration script
---
nova/compute/manager.py | 2 +-
.../versions/007_add_live_migration.py | 83 ++++++++++++++++++++++
2 files changed, 84 insertions(+), 1 deletion(-)
create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_live_migration.py
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 8b90ffbca..d085a0b6a 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -818,7 +818,7 @@ class ComputeManager(manager.Manager):
LOG.info(_('Migrating %(i_name)s to %(dest)s finishes successfully.')
% locals())
- LOG.info(_("The below error is normally occurs."
+ LOG.info(_("The below error is normally occurs. "
"Just check if instance is successfully migrated.\n"
"libvir: QEMU error : Domain not found: no domain "
"with matching name.."))
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_live_migration.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_live_migration.py
new file mode 100644
index 000000000..2689b5b74
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_live_migration.py
@@ -0,0 +1,83 @@
+# 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.
+
+from migrate import *
+from nova import log as logging
+from sqlalchemy import *
+
+
+meta = MetaData()
+
+instances = Table('instances', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+#
+# New Tables
+#
+
+compute_services = Table('compute_services', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('service_id', Integer(), nullable=False),
+
+ Column('vcpus', Integer(), nullable=False),
+ Column('memory_mb', Integer(), nullable=False),
+ Column('local_gb', Integer(), nullable=False),
+ Column('vcpus_used', Integer(), nullable=False),
+ Column('memory_mb_used', Integer(), nullable=False),
+ Column('local_gb_used', Integer(), nullable=False),
+ Column('hypervisor_type',
+ Text(convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False),
+ nullable=False),
+ Column('hypervisor_version', Integer(), nullable=False),
+ Column('cpu_info',
+ Text(convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False),
+ nullable=False),
+ )
+
+
+#
+# Tables to alter
+#
+instances_launched_on = Column(
+ 'launched_on',
+ Text(convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False),
+ nullable=True)
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ try:
+ compute_services.create()
+ except Exception:
+ logging.info(repr(compute_services))
+ logging.exception('Exception while creating table')
+ meta.drop_all(tables=[compute_services])
+ raise
+
+ instances.create_column(instances_launched_on)
--
cgit
From d13a623625a56a029f9dd5ccba3e70f492efdb2c Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Tue, 1 Mar 2011 18:32:57 +0900
Subject: test_compute is changed b/c lack of import instance_types
---
nova/tests/test_compute.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 3c88d186d..2a18dd47b 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -32,6 +32,7 @@ from nova import rpc
from nova import test
from nova import utils
from nova.auth import manager
+from nova.compute import instance_types
from nova.compute import manager as compute_manager
from nova.compute import power_state
from nova.db.sqlalchemy import models
--
cgit
From 688acacd85e07fc578c8731df6a4421e64499c8b Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Tue, 1 Mar 2011 18:53:02 +0900
Subject: At previous commit, I forget to erase conflict - fixed it.
---
nova/tests/test_compute.py | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 75fbc9324..3438719f4 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -309,7 +309,13 @@ class ComputeTestCase(test.TestCase):
self.compute.terminate_instance(self.context, instance_id)
-<<<<<<< TREE
+ def test_get_by_flavor_id(self):
+ type = instance_types.get_by_flavor_id(1)
+ self.assertEqual(type, 'm1.tiny')
+
+ type = instance_types.get_by_flavor_id("1")
+ self.assertEqual(type, 'm1.tiny')
+
def _setup_other_managers(self):
self.volume_manager = utils.import_object(FLAGS.volume_manager)
self.network_manager = utils.import_object(FLAGS.network_manager)
@@ -562,11 +568,3 @@ class ComputeTestCase(test.TestCase):
db.instance_destroy(c, instance_id)
db.volume_destroy(c, v_ref['id'])
db.floating_ip_destroy(c, flo_addr)
-=======
- def test_get_by_flavor_id(self):
- type = instance_types.get_by_flavor_id(1)
- self.assertEqual(type, 'm1.tiny')
-
- type = instance_types.get_by_flavor_id("1")
- self.assertEqual(type, 'm1.tiny')
->>>>>>> MERGE-SOURCE
--
cgit
From f9d08c16d5c620c711d962a78be3a94b99364f14 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 1 Mar 2011 13:56:33 -0500
Subject: support adding a single personality in the osapi
---
nova/api/openstack/servers.py | 22 ++++++++++++++++++++--
nova/tests/api/openstack/test_servers.py | 21 ++++++++++++++++-----
2 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 73c7bfe17..92e5c9024 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -80,7 +80,6 @@ def _translate_detail_keys(inst):
return dict(server=inst_dict)
-
def _translate_keys(inst):
""" Coerces into dictionary format, excluding all model attributes
save for id and name """
@@ -154,6 +153,22 @@ class Controller(wsgi.Controller):
image = self._image_service.show(req.environ['nova.context'], image_id)
return lookup('kernel_id'), lookup('ramdisk_id')
+
+ def _get_onset_files_from_personality_attr(self, personality_attr):
+ """
+ Create a list of onset files from the personality request attribute
+
+ At this time, onset_files must be formatted as a list of
+ (file_path, file_content) pairs for compatibility with the
+ underlying compute service.
+ """
+ onset_files = []
+ for personality in personality_attr:
+ path = personality['path']
+ contents = personality['contents']
+ onset_files.append((path, contents))
+ return onset_files
+
def create(self, req):
""" Creates a new server for a given user """
env = self._deserialize(req.body, req)
@@ -181,6 +196,9 @@ class Controller(wsgi.Controller):
for k, v in env['server']['metadata'].items():
metadata.append({'key': k, 'value': v})
+ personality = env['server'].get('personality', [])
+ onset_files = self._get_onset_files_from_personality_attr(personality)
+
instances = self.compute_api.create(
context,
instance_types.get_by_flavor_id(env['server']['flavorId']),
@@ -192,7 +210,7 @@ class Controller(wsgi.Controller):
key_name=key_pair['name'],
key_data=key_pair['public_key'],
metadata=metadata,
- onset_files=env.get('onset_files', []))
+ onset_files=onset_files)
return _translate_keys(instances[0])
def update(self, req, id):
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 7e5bc0080..42665ba6d 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -232,6 +232,9 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
+ def _personality_dict(self, path, contents):
+ return {'path': path, 'contents': contents}
+
def _create_instance_with_personality(self, personality):
class FakeComputeAPI(object):
@@ -239,7 +242,7 @@ class ServersTest(test.TestCase):
def __init__(self):
self.onset_files = None
- def create(*args, **kwargs):
+ def create(self, *args, **kwargs):
if 'onset_files' in kwargs:
self.onset_files = kwargs['onset_files']
else:
@@ -265,12 +268,20 @@ class ServersTest(test.TestCase):
req = webob.Request.blank('/v1.0/servers')
req.method = 'POST'
req.body = json.dumps(body)
- return req.get_response(fakes.wsgi_app()), compute_api.onset_files
+ return req, req.get_response(fakes.wsgi_app()), compute_api.onset_files
def test_create_instance_with_no_personality(self):
- res, onset_files = self._create_instance_with_personality(personality={})
- self.assertEquals(res.status_int, 200)
- self.assertEquals(onset_files, None)
+ request, response, onset_files = \
+ self._create_instance_with_personality(personality=[])
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(onset_files, [])
+
+ def test_create_instance_with_one_personality(self):
+ personality = [self._personality_dict('/my/path', 'myfilecontents')]
+ request, response, onset_files = \
+ self._create_instance_with_personality(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(onset_files, [('/my/path', 'myfilecontents')])
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
--
cgit
From 94e42c3002f9043fc3c5b90a1cb5ad0c50ba261b Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 1 Mar 2011 16:21:37 -0500
Subject: ensure personality contents are b64 encoded
---
nova/api/openstack/servers.py | 15 ++++++++++-----
nova/tests/api/openstack/test_servers.py | 21 +++++++++++++++++----
2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 8491fe697..8908bbdca 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import base64
import hashlib
import json
import traceback
@@ -138,7 +139,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
- def _get_onset_files_from_personality_attr(self, personality_attr):
+ def _get_onset_files_from_personality(self, personality):
"""
Create a list of onset files from the personality request attribute
@@ -147,9 +148,13 @@ class Controller(wsgi.Controller):
underlying compute service.
"""
onset_files = []
- for personality in personality_attr:
- path = personality['path']
- contents = personality['contents']
+ for item in personality:
+ path = item['path']
+ try:
+ contents = base64.b64decode(item['contents'])
+ except TypeError:
+ raise exc.HTTPBadRequest(explanation=
+ 'Personality content for %s cannot be decoded' % path)
onset_files.append((path, contents))
return onset_files
@@ -181,7 +186,7 @@ class Controller(wsgi.Controller):
metadata.append({'key': k, 'value': v})
personality = env['server'].get('personality', [])
- onset_files = self._get_onset_files_from_personality_attr(personality)
+ onset_files = self._get_onset_files_from_personality(personality)
instances = self.compute_api.create(
context,
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 144dbd4af..4b40793a7 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import base64
import datetime
import json
@@ -272,16 +273,28 @@ class ServersTest(test.TestCase):
def test_create_instance_with_no_personality(self):
request, response, onset_files = \
- self._create_instance_with_personality(personality=[])
+ self._create_instance_with_personality(personality=[])
self.assertEquals(response.status_int, 200)
self.assertEquals(onset_files, [])
- def test_create_instance_with_one_personality(self):
- personality = [self._personality_dict('/my/path', 'myfilecontents')]
+ def test_create_instance_with_personality(self):
+ path = '/my/file/path'
+ contents = '#!/bin/bash\necho "Hello, World!"\n'
+ b64contents = base64.b64encode(contents)
+ personality = [self._personality_dict(path, b64contents)]
request, response, onset_files = \
self._create_instance_with_personality(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [('/my/path', 'myfilecontents')])
+ self.assertEquals(onset_files, [(path, contents)])
+
+ def test_create_instance_with_personality_with_non_b64_content(self):
+ path = '/my/file/path'
+ contents = '#!/bin/bash\necho "Oh no!"\n'
+ personality = [self._personality_dict(path, contents)]
+ request, response, onset_files = \
+ self._create_instance_with_personality(personality)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(onset_files, None)
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
--
cgit
From 7b3ccd5fd1636ebc437a89a3667e6e712004e87f Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 1 Mar 2011 16:48:01 -0500
Subject: test osapi server create with multiple personalities
---
nova/tests/api/openstack/test_servers.py | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 4b40793a7..dd951e90c 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -296,6 +296,20 @@ class ServersTest(test.TestCase):
self.assertEquals(response.status_int, 400)
self.assertEquals(onset_files, None)
+ def test_create_instance_with_two_personalities(self):
+ files = [
+ ('/etc/sudoers', 'ALL ALL=NOPASSWD: ALL\n'),
+ ('/etc/motd', 'Enjoy your root access!\n'),
+ ]
+ personality = []
+ for path, content in files:
+ personality.append(self._personality_dict(
+ path, base64.b64encode(content)))
+ request, response, onset_files = \
+ self._create_instance_with_personality(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(onset_files, files)
+
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
--
cgit
From cdb1b16a6019fd68a7969666d754c4007607ae53 Mon Sep 17 00:00:00 2001
From: Cory Wright
Date: Tue, 1 Mar 2011 23:18:37 +0000
Subject: * Added ability to launch XenServer instances with per-os vm-params.
---
nova/compute/api.py | 5 +-
.../versions/007_add_os_type_to_instances.py | 45 ++++++
nova/db/sqlalchemy/models.py | 2 +
nova/virt/xenapi/vm_utils.py | 152 +++++++++++++++------
nova/virt/xenapi/vmops.py | 17 +--
5 files changed, 164 insertions(+), 57 deletions(-)
create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 625778b66..8bdf712a0 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -125,6 +125,8 @@ class API(base.Base):
raise quota.QuotaError(msg, "MetadataLimitExceeded")
image = self.image_service.show(context, image_id)
+ os_type = image['properties'].get('os_type', 'linux')
+
if kernel_id is None:
kernel_id = image.get('kernel_id', None)
if ramdisk_id is None:
@@ -180,7 +182,8 @@ class API(base.Base):
'key_data': key_data,
'locked': False,
'metadata': metadata,
- 'availability_zone': availability_zone}
+ 'availability_zone': availability_zone,
+ 'os_type': os_type}
elevated = context.elevated()
instances = []
LOG.debug(_("Going to run %s instances..."), num_instances)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
new file mode 100644
index 000000000..d6d964b95
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
@@ -0,0 +1,45 @@
+# 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.
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import log as logging
+
+
+meta = MetaData()
+
+instances = Table('instances', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+# FIXME(dubs) should this be not null? Maybe create as nullable, then
+# populate all existing rows with 'linux', then adding not null constraint.
+instances_os_type = Column('os_type',
+ String(length=255, convert_unicode=False,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False),
+ nullable=True)
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ instances.create_column(instances_os_type)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 1882efeba..b78c95e40 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -188,6 +188,8 @@ class Instance(BASE, NovaBase):
locked = Column(Boolean)
+ os_type = Column(String(255))
+
# TODO(vish): see Ewan's email about state improvements, probably
# should be in a driver base class or some such
# vmstate_state = running, halted, suspended, paused
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 6d9aeb060..11f1fabe9 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -80,62 +80,80 @@ class VMHelper(HelperBase):
"""
@classmethod
- def create_vm(cls, session, instance, kernel, ramdisk, pv_kernel=False):
+ def create_vm(cls, session, instance, kernel, ramdisk, use_pv_kernel=False):
"""Create a VM record. Returns a Deferred that gives the new
VM reference.
- the pv_kernel flag indicates whether the guest is HVM or PV
+ the use_pv_kernel flag indicates whether the guest is HVM or PV
+
+ There are 3 scenarios:
+
+ 1. Using paravirtualization, kernel passed in
+
+ 2. Using paravirtualization, kernel within the image
+
+ 3. Using hardware virtualization
"""
instance_type = instance_types.INSTANCE_TYPES[instance.instance_type]
mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
vcpus = str(instance_type['vcpus'])
rec = {
- 'name_label': instance.name,
- 'name_description': '',
+ 'actions_after_crash': 'destroy',
+ 'actions_after_reboot': 'restart',
+ 'actions_after_shutdown': 'destroy',
+ 'affinity': '',
+ 'blocked_operations': {},
+ 'ha_always_run': False,
+ 'ha_restart_priority': '',
+ 'HVM_boot_params': {},
+ 'HVM_boot_policy': '',
'is_a_template': False,
- 'memory_static_min': '0',
- 'memory_static_max': mem,
'memory_dynamic_min': mem,
'memory_dynamic_max': mem,
- 'VCPUs_at_startup': vcpus,
- 'VCPUs_max': vcpus,
- 'VCPUs_params': {},
- 'actions_after_shutdown': 'destroy',
- 'actions_after_reboot': 'restart',
- 'actions_after_crash': 'destroy',
- 'PV_bootloader': '',
- 'PV_kernel': '',
- 'PV_ramdisk': '',
+ 'memory_static_min': '0',
+ 'memory_static_max': mem,
+ 'memory_target': mem,
+ 'name_description': '',
+ 'name_label': instance.name,
+# 'other_config': {'allowvssprovider': False},
+ 'other_config': {},
+ 'PCI_bus': '',
+ 'platform': {'acpi': 'true', 'apic': 'true', 'pae': 'true',
+ 'viridian': 'true', 'timeoffset': '0'},
'PV_args': '',
+ 'PV_bootloader': '',
'PV_bootloader_args': '',
+ 'PV_kernel': '',
'PV_legacy_args': '',
- 'HVM_boot_policy': '',
- 'HVM_boot_params': {},
- 'platform': {},
- 'PCI_bus': '',
+ 'PV_ramdisk': '',
'recommendations': '',
- 'affinity': '',
+ 'tags': [],
'user_version': '0',
- 'other_config': {},
+ 'VCPUs_at_startup': vcpus,
+ 'VCPUs_max': vcpus,
+ 'VCPUs_params': {},
+ 'xenstore_data': {}
}
- #Complete VM configuration record according to the image type
- #non-raw/raw with PV kernel/raw in HVM mode
- if instance.kernel_id:
- rec['PV_bootloader'] = ''
- rec['PV_kernel'] = kernel
- rec['PV_ramdisk'] = ramdisk
- rec['PV_args'] = 'root=/dev/xvda1'
- rec['PV_bootloader_args'] = ''
- rec['PV_legacy_args'] = ''
- else:
- if pv_kernel:
- rec['PV_args'] = 'noninteractive'
- rec['PV_bootloader'] = 'pygrub'
+
+ # Complete VM configuration record according to the image type
+ # non-raw/raw with PV kernel/raw in HVM mode
+ if use_pv_kernel:
+ rec['platform']['nx'] = 'false'
+ if instance.kernel_id:
+ # 1. Kernel explicitly passed in, use that
+ rec['PV_args'] = 'root=/dev/xvda1'
+ rec['PV_kernel'] = kernel
+ rec['PV_ramdisk'] = ramdisk
else:
- rec['HVM_boot_policy'] = 'BIOS order'
- rec['HVM_boot_params'] = {'order': 'dc'}
- rec['platform'] = {'acpi': 'true', 'apic': 'true',
- 'pae': 'true', 'viridian': 'true'}
+ # 2. Use kernel within the image
+ rec['PV_args'] = 'clocksource=jiffies'
+ rec['PV_bootloader'] = 'pygrub'
+ else:
+ # 3. Using hardware virtualization
+ rec['platform']['nx'] = 'true'
+ rec['HVM_boot_params'] = {'order': 'dc'}
+ rec['HVM_boot_policy'] = 'BIOS order'
+
LOG.debug(_('Created VM %s...'), instance.name)
vm_ref = session.call_xenapi('VM.create', rec)
instance_name = instance.name
@@ -497,17 +515,32 @@ class VMHelper(HelperBase):
return uuid
@classmethod
- def lookup_image(cls, session, instance_id, vdi_ref):
+ def determine_is_pv(cls, session, instance_id, vdi_ref, disk_image_type,
+ os_type):
"""
- Determine if VDI is using a PV kernel
+ Determine whether the VM will use a paravirtualized kernel or if it
+ will use hardware virtualization.
+
+ 1. Objectstore (any image type):
+ We use plugin to figure out whether the VDI uses PV
+
+ 2. Glance (VHD): then we use `os_type`, raise if not set
+
+ 3. Glance (DISK_RAW): use Pygrub to figure out if pv kernel is
+ available
+
+ 4. Glance (DISK): pv is assumed
"""
if FLAGS.xenapi_image_service == 'glance':
- return cls._lookup_image_glance(session, vdi_ref)
+ # 2, 3, 4: Glance
+ return cls._determine_is_pv_glance(
+ session, vdi_ref, disk_image_type, os_type)
else:
- return cls._lookup_image_objectstore(session, instance_id, vdi_ref)
+ # 1. Objecstore
+ return cls._determine_is_pv_objectstore(session, instance_id, vdi_ref)
@classmethod
- def _lookup_image_objectstore(cls, session, instance_id, vdi_ref):
+ def _determine_is_pv_objectstore(cls, session, instance_id, vdi_ref):
LOG.debug(_("Looking up vdi %s for PV kernel"), vdi_ref)
fn = "is_vdi_pv"
args = {}
@@ -523,9 +556,38 @@ class VMHelper(HelperBase):
return pv
@classmethod
- def _lookup_image_glance(cls, session, vdi_ref):
+ def _determine_is_pv_glance(cls, session, vdi_ref, disk_image_type,
+ os_type):
+ """
+ For a Glance image, determine if we need paravirtualization.
+
+ The relevant scenarios are:
+ 2. Glance (VHD): then we use `os_type`, raise if not set
+
+ 3. Glance (DISK_RAW): use Pygrub to figure out if pv kernel is
+ available
+
+ 4. Glance (DISK): pv is assumed
+ """
+
LOG.debug(_("Looking up vdi %s for PV kernel"), vdi_ref)
- return with_vdi_attached_here(session, vdi_ref, True, _is_vdi_pv)
+ if disk_image_type == ImageType.DISK_VHD:
+ # 2. VHD
+ if os_type == 'windows':
+ is_pv = False
+ else:
+ is_pv = True
+ elif disk_image_type == ImageType.DISK_RAW:
+ # 3. RAW
+ is_pv = with_vdi_attached_here(session, vdi_ref, True, _is_vdi_pv)
+ elif disk_image_type == ImageType.DISK:
+ # 4. Disk
+ is_pv = True
+ else:
+ raise exception.Error(_("Unknown image format %(disk_image_type)s")
+ % locals())
+
+ return is_pv
@classmethod
def lookup(cls, session, i):
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index bc39aa140..abc1fb699 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -87,15 +87,7 @@ class VMOps(object):
vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
- pv_kernel = False
- if disk_image_type == ImageType.DISK_RAW:
- #Have a look at the VDI and see if it has a PV kernel
- pv_kernel = VMHelper.lookup_image(self._session, instance.id,
- vdi_ref)
- elif disk_image_type == ImageType.DISK_VHD:
- # TODO(sirp): Assuming PV for now; this will need to be
- # configurable as Windows will use HVM.
- pv_kernel = True
+ os_type = instance.get('os_type', 'linux')
kernel = None
if instance.kernel_id:
@@ -107,8 +99,11 @@ class VMOps(object):
ramdisk = VMHelper.fetch_image(self._session, instance.id,
instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK)
- vm_ref = VMHelper.create_vm(self._session,
- instance, kernel, ramdisk, pv_kernel)
+ use_pv_kernel = VMHelper.determine_is_pv(
+ self._session, instance.id, vdi_ref, disk_image_type, os_type)
+ vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk,
+ use_pv_kernel)
+
VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True)
# inject_network_info and create vifs
--
cgit
From 6321c5047c082bba8edf10a660fdb6a56430cc44 Mon Sep 17 00:00:00 2001
From: Cory Wright
Date: Wed, 2 Mar 2011 00:19:02 +0000
Subject: * Added first cut of migration for os_type on instances table * Track
os_type when taking snapshots
---
.../migrate_repo/versions/007_add_os_type_to_instances.py | 4 +++-
nova/virt/xenapi/vm_utils.py | 9 ++++++---
nova/virt/xenapi/vmops.py | 4 ++--
plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 11 +++++++----
4 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
index d6d964b95..21f21b040 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
@@ -34,7 +34,7 @@ instances_os_type = Column('os_type',
String(length=255, convert_unicode=False,
assert_unicode=None, unicode_error=None,
_warn_on_bytestring=False),
- nullable=True)
+ nullable=False)
def upgrade(migrate_engine):
@@ -43,3 +43,5 @@ def upgrade(migrate_engine):
meta.bind = migrate_engine
instances.create_column(instances_os_type)
+
+
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 11f1fabe9..9c0bb5579 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -303,7 +303,7 @@ class VMHelper(HelperBase):
return template_vm_ref, template_vdi_uuids
@classmethod
- def upload_image(cls, session, instance_id, vdi_uuids, image_id):
+ def upload_image(cls, session, instance, vdi_uuids, image_id):
""" Requests that the Glance plugin bundle the specified VDIs and
push them into Glance using the specified human-friendly name.
"""
@@ -312,15 +312,18 @@ class VMHelper(HelperBase):
logging.debug(_("Asking xapi to upload %(vdi_uuids)s as"
" ID %(image_id)s") % locals())
+ # TODO(dubs): os_type is currently defaulting to linux, we actually
+ # want to make this a NOT NULL column and require it to be specified.
params = {'vdi_uuids': vdi_uuids,
'image_id': image_id,
'glance_host': FLAGS.glance_host,
'glance_port': FLAGS.glance_port,
- 'sr_path': get_sr_path(session)}
+ 'sr_path': get_sr_path(session),
+ 'os_type': instance.get('os_type', 'linux')}
kwargs = {'params': pickle.dumps(params)}
task = session.async_call_plugin('glance', 'upload_vhd', kwargs)
- session.wait_for_task(instance_id, task)
+ session.wait_for_task(instance.id, task)
@classmethod
def fetch_image(cls, session, instance_id, image, user, project,
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index abc1fb699..1edf39c5b 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -238,11 +238,11 @@ class VMOps(object):
try:
# call plugin to ship snapshot off to glance
VMHelper.upload_image(
- self._session, instance.id, template_vdi_uuids, image_id)
+ self._session, instance, template_vdi_uuids, image_id)
finally:
self._destroy(instance, template_vm_ref, shutdown=False,
destroy_kernel_ramdisk=False)
-
+
logging.debug(_("Finished snapshot and upload for VM %s"), instance)
def reboot(self, instance):
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index 7531af4ec..160bf482f 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -191,7 +191,7 @@ def _prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids):
os.link(source, link_name)
-def _upload_tarball(staging_path, image_id, glance_host, glance_port):
+def _upload_tarball(staging_path, image_id, glance_host, glance_port, os_type):
"""
Create a tarball of the image and then stream that into Glance
using chunked-transfer-encoded HTTP.
@@ -205,9 +205,10 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port):
headers = {
'content-type': 'application/octet-stream',
'transfer-encoding': 'chunked',
- 'x-image-meta-is_public': 'True',
+ 'x-image-meta-is-public': 'True',
'x-image-meta-status': 'queued',
- 'x-image-meta-type': 'vhd'
+ 'x-image-meta-type': 'vhd',
+ 'x-image-meta-property-os-type': os_type
}
for header, value in headers.iteritems():
conn.putheader(header, value)
@@ -330,11 +331,13 @@ def upload_vhd(session, args):
glance_host = params["glance_host"]
glance_port = params["glance_port"]
sr_path = params["sr_path"]
+ os_type = params["os_type"]
staging_path = _make_staging_area(sr_path)
try:
_prepare_staging_area_for_upload(sr_path, staging_path, vdi_uuids)
- _upload_tarball(staging_path, image_id, glance_host, glance_port)
+ _upload_tarball(staging_path, image_id, glance_host, glance_port,
+ os_type)
finally:
_cleanup_staging_area(staging_path)
--
cgit
From df0a4d66f7059db94e1de365fed8b8d244e16534 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Tue, 1 Mar 2011 17:12:47 -0800
Subject: Changed ra_server to gateway_v6 and removed addressv6 column from
fixed_ips db table
---
nova/api/ec2/cloud.py | 4 ---
nova/db/sqlalchemy/api.py | 11 +++---
.../versions/007_add_ipv6_flatmanager.py | 24 ++++---------
nova/db/sqlalchemy/models.py | 5 ++-
nova/network/linux_net.py | 2 +-
nova/network/manager.py | 37 +++++---------------
nova/virt/interfaces.template | 6 ++--
nova/virt/libvirt.xml.template | 4 +--
nova/virt/libvirt_conn.py | 40 +++++++++++-----------
9 files changed, 47 insertions(+), 86 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 15799670c..b3779a4da 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -701,10 +701,6 @@ class CloudController(object):
fixed = instance['fixed_ip']
floating_addr = fixed['floating_ips'][0]['address']
if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
- if FLAGS.network_manager == \
- 'nova.network.manager.FlatManager':
- i['dnsNameV6'] = instance['fixed_ip']['addressv6']
- else:
i['dnsNameV6'] = utils.to_global_ipv6(
instance['fixed_ip']['network']['cidr_v6'],
instance['mac_address'])
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 9bc59de60..d8751bef4 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -833,13 +833,10 @@ def instance_get_fixed_address_v6(context, instance_id):
session = get_session()
with session.begin():
instance_ref = instance_get(context, instance_id, session=session)
- if 'nova.network.manager.FlatManager' == FLAGS.network_manager:
- return instance_ref.fixed_ip['addressv6']
- else:
- network_ref = network_get_by_instance(context, instance_id)
- prefix = network_ref.cidr_v6
- mac = instance_ref.mac_address
- return utils.to_global_ipv6(prefix, mac)
+ network_ref = network_get_by_instance(context, instance_id)
+ prefix = network_ref.cidr_v6
+ mac = instance_ref.mac_address
+ return utils.to_global_ipv6(prefix, mac)
@require_context
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
index 2951cbc0a..e09f46652 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
@@ -30,10 +30,6 @@ networks = Table('networks', meta,
Column('id', Integer(), primary_key=True, nullable=False),
)
-fixed_ips = Table('fixed_ips', meta,
- Column('id', Integer(), primary_key=True, nullable=False),
- )
-
#
# New Tables
#
@@ -42,24 +38,19 @@ fixed_ips = Table('fixed_ips', meta,
#
# Tables to alter
#
-# None
+
#
# Columns to add to existing tables
#
-networks_gatewayv6 = Column(
- 'gatewayv6',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False))
-
-networks_netmaskv6 = Column(
- 'netmaskv6',
+networks_gateway_v6 = Column(
+ 'gateway_v6',
String(length=255, convert_unicode=False, assert_unicode=None,
unicode_error=None, _warn_on_bytestring=False))
-fixed_ips_addressv6 = Column(
- 'addressv6',
+networks_netmask_v6 = Column(
+ 'netmask_v6',
String(length=255, convert_unicode=False, assert_unicode=None,
unicode_error=None, _warn_on_bytestring=False))
@@ -70,6 +61,5 @@ def upgrade(migrate_engine):
meta.bind = migrate_engine
# Add columns to existing tables
- networks.create_column(networks_gatewayv6)
- networks.create_column(networks_netmaskv6)
- fixed_ips.create_column(fixed_ips_addressv6)
+ networks.create_column(networks_gateway_v6)
+ networks.create_column(networks_netmask_v6)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 4fa4d443c..f235054d2 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -385,8 +385,8 @@ class Network(BASE, NovaBase):
ra_server = Column(String(255))
- gatewayv6 = Column(String(255))
- netmaskv6 = Column(String(255))
+ gateway_v6 = Column(String(255))
+ netmask_v6 = Column(String(255))
netmask = Column(String(255))
bridge = Column(String(255))
gateway = Column(String(255))
@@ -427,7 +427,6 @@ class FixedIp(BASE, NovaBase):
__tablename__ = 'fixed_ips'
id = Column(Integer, primary_key=True)
address = Column(String(255))
- addressv6 = Column(String(255))
network_id = Column(Integer, ForeignKey('networks.id'), nullable=True)
network = relationship(Network, backref=backref('fixed_ips'))
instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True)
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 535ce87bc..e375568f1 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -361,7 +361,7 @@ interface %s
command = _ra_cmd(network_ref)
_execute(command)
db.network_update(context, network_id,
- {"ra_server":
+ {"gateway_v6":
utils.get_my_linklocal(network_ref['bridge'])})
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 61b5dc07f..bdeeae293 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -388,41 +388,14 @@ class FlatManager(NetworkManager):
significant_bits_v6)
net['cidr_v6'] = cidr_v6
project_net_v6 = IPy.IP(cidr_v6)
- net['gatewayv6'] = str(project_net_v6[1])
- net['netmaskv6'] = str(project_net_v6.prefixlen())
+ net['gateway_v6'] = str(project_net_v6[1])
+ net['netmask_v6'] = str(project_net_v6.prefixlen())
network_ref = self.db.network_create_safe(context, net)
if network_ref:
self._create_fixed_ips(context, network_ref['id'])
- def _create_fixed_ips(self, context, network_id):
- """Create all fixed ips for network."""
- network_ref = self.db.network_get(context, network_id)
- # NOTE(vish): Should these be properties of the network as opposed
- # to properties of the manager class?
- bottom_reserved = self._bottom_reserved_ips
- top_reserved = self._top_reserved_ips
- project_net = IPy.IP(network_ref['cidr'])
-
- if(FLAGS.use_ipv6):
- project_net_v6 = IPy.IP(network_ref['cidr_v6'])
-
- num_ips = len(project_net)
- addressv6 = None
- for index in range(num_ips):
- address = str(project_net[index])
- if(FLAGS.use_ipv6):
- addressv6 = str(project_net_v6[index])
- if index < bottom_reserved or num_ips - index < top_reserved:
- reserved = True
- else:
- reserved = False
- self.db.fixed_ip_create(context, {'network_id': network_id,
- 'address': address,
- 'addressv6': addressv6,
- 'reserved': reserved})
-
def get_network_host(self, context):
"""Get the network host for the current context."""
network_ref = self.db.network_get_by_bridge(context,
@@ -448,6 +421,12 @@ class FlatManager(NetworkManager):
net = {}
net['injected'] = FLAGS.flat_injected
net['dns'] = FLAGS.flat_network_dns
+
+ if not FLAGS.fake_network:
+ if(FLAGS.use_ipv6):
+ net['gateway_v6'] = \
+ utils.get_my_linklocal(FLAGS.flat_network_bridge)
+
self.db.network_update(context, network_id, net)
def allocate_floating_ip(self, context, project_id):
diff --git a/nova/virt/interfaces.template b/nova/virt/interfaces.template
index 1db745f9f..3b34e54f4 100644
--- a/nova/virt/interfaces.template
+++ b/nova/virt/interfaces.template
@@ -16,8 +16,8 @@ iface eth0 inet static
#if $use_ipv6
iface eth0 inet6 static
- address ${addressv6}
- netmask ${netmaskv6}
- gateway ${gatewayv6}
+ address ${address_v6}
+ netmask ${netmask_v6}
+ gateway ${gateway_v6}
#end if
diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template
index 88bfbc668..ef2d2cd6b 100644
--- a/nova/virt/libvirt.xml.template
+++ b/nova/virt/libvirt.xml.template
@@ -79,8 +79,8 @@
#if $getVar('extra_params', False)
${extra_params}
#end if
-#if $getVar('ra_server', False)
-
+#if $getVar('gateway_v6', False)
+
#end if
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index b7712f76e..38fa2338e 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -631,21 +631,21 @@ class LibvirtConnection(object):
admin_context = context.get_admin_context()
address = db.instance_get_fixed_address(admin_context,
inst['id'])
- addressv6 = db.instance_get_fixed_address_v6(admin_context,
+ address_v6 = db.instance_get_fixed_address_v6(admin_context,
inst['id'])
- ra_server = network_ref['ra_server']
- if not ra_server:
- ra_server = "fd00::"
+ gateway_v6 = network_ref['gateway_v6']
+ if not gateway_v6:
+ gateway_v6 = "fd00::"
interfaces_info = {'address': address,
'netmask': network_ref['netmask'],
'gateway': network_ref['gateway'],
'broadcast': network_ref['broadcast'],
'dns': network_ref['dns'],
- 'ra_server': ra_server,
- 'addressv6': addressv6,
- 'gatewayv6': network_ref['gatewayv6'],
- 'netmaskv6': network_ref['netmaskv6'],
+ 'gateway_v6': gateway_v6,
+ 'address_v6': address_v6,
+ 'gateway_v6': network_ref['gateway_v6'],
+ 'netmask_v6': network_ref['netmask_v6'],
'use_ipv6': FLAGS.use_ipv6}
net = str(Template(self.interfaces_xml,
@@ -683,7 +683,7 @@ class LibvirtConnection(object):
instance['id'])
# Assume that the gateway also acts as the dhcp server.
dhcp_server = network['gateway']
- ra_server = network['ra_server']
+ gateway_v6 = network['gateway_v6']
if FLAGS.allow_project_net_traffic:
if FLAGS.use_ipv6:
@@ -728,8 +728,8 @@ class LibvirtConnection(object):
'local': instance_type['local_gb'],
'driver_type': driver_type}
- if ra_server:
- xml_info['ra_server'] = ra_server + "/128"
+ if gateway_v6:
+ xml_info['gateway_v6'] = gateway_v6 + "/128"
if not rescue:
if instance['kernel_id']:
xml_info['kernel'] = xml_info['basepath'] + "/kernel"
@@ -921,10 +921,10 @@ class FirewallDriver(object):
"""
raise NotImplementedError()
- def _ra_server_for_instance(self, instance):
+ def _gateway_v6_for_instance(self, instance):
network = db.network_get_by_instance(context.get_admin_context(),
instance['id'])
- return network['ra_server']
+ return network['gateway_v6']
class NWFilterFirewall(FirewallDriver):
@@ -1140,8 +1140,8 @@ class NWFilterFirewall(FirewallDriver):
'nova-base-ipv6',
'nova-allow-dhcp-server']
if FLAGS.use_ipv6:
- ra_server = self._ra_server_for_instance(instance)
- if ra_server:
+ gateway_v6 = self._gateway_v6_for_instance(instance)
+ if gateway_v6:
instance_secgroup_filter_children += ['nova-allow-ra-server']
ctxt = context.get_admin_context()
@@ -1328,10 +1328,10 @@ class IptablesFirewallDriver(FirewallDriver):
our_rules += ['-A %s -s %s -j ACCEPT' % (chain_name, cidr)]
elif(ip_version == 6):
# Allow RA responses
- ra_server = self._ra_server_for_instance(instance)
- if ra_server:
+ gateway_v6 = self._gateway_v6_for_instance(instance)
+ if gateway_v6:
our_rules += ['-A %s -s %s -p icmpv6 -j ACCEPT' %
- (chain_name, ra_server + "/128")]
+ (chain_name, gateway_v6 + "/128")]
#Allow project network traffic
if (FLAGS.allow_project_net_traffic):
cidrv6 = self._project_cidrv6_for_instance(instance)
@@ -1427,10 +1427,10 @@ class IptablesFirewallDriver(FirewallDriver):
instance['id'])
return network['gateway']
- def _ra_server_for_instance(self, instance):
+ def _gateway_v6_for_instance(self, instance):
network = db.network_get_by_instance(context.get_admin_context(),
instance['id'])
- return network['ra_server']
+ return network['gateway_v6']
def _project_cidr_for_instance(self, instance):
network = db.network_get_by_instance(context.get_admin_context(),
--
cgit
From d33866923958b3529a812f4eef7dea4a6591a423 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Wed, 2 Mar 2011 17:36:21 -0500
Subject: add support for quotas on file injection
---
nova/quota.py | 29 +++++++++++++++++++++++------
nova/tests/test_quota.py | 10 ++++++++++
2 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/nova/quota.py b/nova/quota.py
index 6b52a97fa..14b388794 100644
--- a/nova/quota.py
+++ b/nova/quota.py
@@ -37,15 +37,22 @@ flags.DEFINE_integer('quota_floating_ips', 10,
'number of floating ips allowed per project')
flags.DEFINE_integer('quota_metadata_items', 128,
'number of metadata items allowed per instance')
+flags.DEFINE_integer('quota_file_injection_max_files', 5,
+ 'number of files allowed during file injection')
+flags.DEFINE_integer('quota_file_injection_max_file_bytes', 10 * 1024,
+ 'number of bytes allowed per file during file injection')
def get_quota(context, project_id):
- rval = {'instances': FLAGS.quota_instances,
- 'cores': FLAGS.quota_cores,
- 'volumes': FLAGS.quota_volumes,
- 'gigabytes': FLAGS.quota_gigabytes,
- 'floating_ips': FLAGS.quota_floating_ips,
- 'metadata_items': FLAGS.quota_metadata_items}
+ rval = {
+ 'instances': FLAGS.quota_instances,
+ 'cores': FLAGS.quota_cores,
+ 'volumes': FLAGS.quota_volumes,
+ 'gigabytes': FLAGS.quota_gigabytes,
+ 'floating_ips': FLAGS.quota_floating_ips,
+ 'metadata_items': FLAGS.quota_metadata_items,
+ }
+
try:
quota = db.quota_get(context, project_id)
for key in rval.keys():
@@ -106,6 +113,16 @@ def allowed_metadata_items(context, num_metadata_items):
return min(num_metadata_items, num_allowed_metadata_items)
+def allowed_file_injection_files(context):
+ """Return the number of files allowed per file injection"""
+ return FLAGS.quota_file_injection_max_files
+
+
+def allowed_file_injection_file_bytes(context):
+ """Return the number of bytes allowed per file during injection"""
+ return FLAGS.quota_file_injection_max_file_bytes
+
+
class QuotaError(exception.ApiError):
"""Quota Exceeeded"""
pass
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index 1e42fddf3..48e5a5538 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -176,3 +176,13 @@ class QuotaTestCase(test.TestCase):
instance_type='m1.small',
image_id='fake',
metadata=metadata)
+
+ def test_allowed_file_injection_files(self):
+ self.assertEqual(
+ quota.allowed_file_injection_files(self.context),
+ FLAGS.quota_file_injection_max_files)
+
+ def test_allowed_file_injection_file_bytes(self):
+ self.assertEqual(
+ quota.allowed_file_injection_file_bytes(self.context),
+ FLAGS.quota_file_injection_max_file_bytes)
--
cgit
From 668cdc96b3f6fb412b9d1d4a3780744d6b2340b1 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Thu, 3 Mar 2011 00:59:09 -0500
Subject: more rigorous testing and error handling for os api personality
---
nova/api/openstack/servers.py | 8 ++++++--
nova/tests/api/openstack/test_servers.py | 25 +++++++++++++++++++++++--
2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 8908bbdca..73c787828 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -149,9 +149,13 @@ class Controller(wsgi.Controller):
"""
onset_files = []
for item in personality:
- path = item['path']
try:
- contents = base64.b64decode(item['contents'])
+ path = item['path']
+ contents = item['contents']
+ except TypeError:
+ raise exc.HTTPBadRequest(explanation='Bad personality format')
+ try:
+ contents = base64.b64decode(contents)
except TypeError:
raise exc.HTTPBadRequest(explanation=
'Personality content for %s cannot be decoded' % path)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index dd951e90c..272d34e3a 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -265,6 +265,8 @@ class ServersTest(test.TestCase):
name='server_test', imageId=2, flavorId=2,
metadata={},
personality=personality))
+ if personality is None:
+ del body['server']['personality']
req = webob.Request.blank('/v1.0/servers')
req.method = 'POST'
@@ -273,7 +275,7 @@ class ServersTest(test.TestCase):
def test_create_instance_with_no_personality(self):
request, response, onset_files = \
- self._create_instance_with_personality(personality=[])
+ self._create_instance_with_personality(personality=None)
self.assertEquals(response.status_int, 200)
self.assertEquals(onset_files, [])
@@ -296,10 +298,11 @@ class ServersTest(test.TestCase):
self.assertEquals(response.status_int, 400)
self.assertEquals(onset_files, None)
- def test_create_instance_with_two_personalities(self):
+ def test_create_instance_with_three_personalities(self):
files = [
('/etc/sudoers', 'ALL ALL=NOPASSWD: ALL\n'),
('/etc/motd', 'Enjoy your root access!\n'),
+ ('/etc/dovecot.conf', 'dovecot\nconfig\nstuff\n'),
]
personality = []
for path, content in files:
@@ -310,6 +313,24 @@ class ServersTest(test.TestCase):
self.assertEquals(response.status_int, 200)
self.assertEquals(onset_files, files)
+ def test_create_instance_personality_empty_content(self):
+ path = '/my/file/path'
+ contents = ''
+ personality = [self._personality_dict(path, contents)]
+ request, response, onset_files = \
+ self._create_instance_with_personality(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(onset_files, [(path, contents)])
+
+ def test_create_instance_personality_not_a_list(self):
+ path = '/my/file/path'
+ contents = 'myfilecontents'
+ personality = self._personality_dict(path, contents)
+ request, response, onset_files = \
+ self._create_instance_with_personality(personality)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(onset_files, None)
+
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
--
cgit
From 693e4335dbef72317147abd70bdaa10e0d174020 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Thu, 3 Mar 2011 22:54:11 +0900
Subject: Fixed based on reviewer's comments. Main changes are below. 1. Rename
nova.compute.manager.ComputeManager.mktmpfile for better naming. 2. Several
tests code in tests/test_virt.py are removed. Because it only works in
libvirt environment. Only db-related testcode remains.
---
nova/compute/manager.py | 53 ++++---
nova/scheduler/driver.py | 74 +++++----
nova/scheduler/manager.py | 18 +--
nova/tests/test_scheduler.py | 14 +-
nova/tests/test_virt.py | 363 +++++++++----------------------------------
nova/virt/libvirt_conn.py | 10 +-
nova/volume/manager.py | 4 +-
7 files changed, 166 insertions(+), 370 deletions(-)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index d085a0b6a..7104daa1e 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -624,11 +624,12 @@ class ComputeManager(manager.Manager):
return self.driver.compare_cpu(cpu_info)
@exception.wrap_exception
- def mktmpfile(self, context):
+ def create_shared_storage_test_file(self, context):
"""Makes tmpfile under FLAGS.instance_path.
This method enables compute nodes to recognize that they mounts
- same shared storage. mktmpfile()/confirm_tmpfile is a pair.
+ same shared storage. (create|check|creanup)_shared_storage_test_file()
+ is a pair.
:param context: security context
:returns: tmpfile name(basename)
@@ -636,26 +637,36 @@ class ComputeManager(manager.Manager):
"""
dirpath = FLAGS.instances_path
- fd, name = tempfile.mkstemp(dir=dirpath)
+ fd, tmp_file = tempfile.mkstemp(dir=dirpath)
LOG.debug(_("Creating tmpfile %s to notify to other "
- "compute node that they mounts same storage.") % name)
+ "compute node that they mounts same storage.") % tmp_file)
os.fdopen(fd, 'w+').close()
- return os.path.basename(name)
+ return os.path.basename(tmp_file)
@exception.wrap_exception
- def confirm_tmpfile(self, context, filename):
- """Confirms existence of the tmpfile given by path.
+ def check_shared_storage_test_file(self, context, filename):
+ """Confirms existence of the tmpfile under FLAGS.instances_path.
:param context: security context
:param filename: confirm existence of FLAGS.instances_path/thisfile
- :returns: depends on os.remove()
"""
- p = os.path.join(FLAGS.instances_path, filename)
- if not os.path.exists(p):
- raise exception.NotFound(_('%s not found') % p)
- return os.remove(p)
+ tmp_file = os.path.join(FLAGS.instances_path, filename)
+ if not os.path.exists(tmp_file):
+ raise exception.NotFound(_('%s not found') % tmp_file)
+
+ @exception.wrap_exception
+ def cleanup_shared_storage_test_file(self, context, filename):
+ """Removes existence of the tmpfile under FLAGS.instances_path.
+
+ :param context: security context
+ :param filename: remove existence of FLAGS.instances_path/thisfile
+
+ """
+
+ tmp_file = os.path.join(FLAGS.instances_path, filename)
+ os.remove(tmp_file)
@exception.wrap_exception
def update_available_resource(self, context):
@@ -687,7 +698,7 @@ class ComputeManager(manager.Manager):
raise exception.NotFound(msg % locals())
# If any volume is mounted, prepare here.
- if len(instance_ref['volumes']) == 0:
+ if not instance_ref['volumes']:
LOG.info(_("%s has no volume."), ec2_id)
else:
for v in instance_ref['volumes']:
@@ -701,16 +712,16 @@ class ComputeManager(manager.Manager):
# Retry operation is necessary because continuously request comes,
# concorrent request occurs to iptables, then it complains.
max_retry = FLAGS.live_migration_retry_count
- for i in range(max_retry):
+ for cnt in range(max_retry):
try:
self.network_manager.setup_compute_network(context,
instance_id)
break
- except exception.ProcessExecutionError, e:
- if i == max_retry - 1:
+ except exception.ProcessExecutionError:
+ if cnt == max_retry - 1:
raise
else:
- LOG.warn(_("setup_compute_network() failed %(i)d."
+ LOG.warn(_("setup_compute_network() failed %(cnt)d."
"Retry up to %(max_retry)d for %(ec2_id)s.")
% locals())
time.sleep(1)
@@ -739,7 +750,7 @@ class ComputeManager(manager.Manager):
try:
# Checking volume node is working correctly when any volumes
# are attached to instances.
- if len(instance_ref['volumes']) != 0:
+ if instance_ref['volumes']:
rpc.call(context,
FLAGS.volume_topic,
{"method": "check_for_export",
@@ -751,7 +762,7 @@ class ComputeManager(manager.Manager):
{"method": "pre_live_migration",
"args": {'instance_id': instance_id}})
- except Exception, e:
+ except Exception:
msg = _("Pre live migration for %(i_name)s failed at %(dest)s")
LOG.error(msg % locals())
self.recover_live_migration(context, instance_ref)
@@ -843,5 +854,5 @@ class ComputeManager(manager.Manager):
'state': power_state.RUNNING,
'host': host})
- for v in instance_ref['volumes']:
- self.db.volume_update(ctxt, v['id'], {'status': 'in-use'})
+ for volume in instance_ref['volumes']:
+ self.db.volume_update(ctxt, volume['id'], {'status': 'in-use'})
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 73ce651da..4485ba39f 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -100,9 +100,9 @@ class Scheduler(object):
'migrating')
# Changing volume state
- for v in instance_ref['volumes']:
+ for volume_ref in instance_ref['volumes']:
db.volume_update(context,
- v['id'],
+ volume_ref['id'],
{'status': 'migrating'})
# Return value is necessary to send request to src
@@ -121,17 +121,16 @@ class Scheduler(object):
# Checking instance is running.
if (power_state.RUNNING != instance_ref['state'] or \
'running' != instance_ref['state_description']):
- msg = _('Instance(%s) is not running')
ec2_id = instance_ref['hostname']
- raise exception.Invalid(msg % ec2_id)
+ raise exception.Invalid(_('Instance(%s) is not running') % ec2_id)
# Checing volume node is running when any volumes are mounted
# to the instance.
if len(instance_ref['volumes']) != 0:
services = db.service_get_all_by_topic(context, 'volume')
if len(services) < 1 or not self.service_is_up(services[0]):
- msg = _('volume node is not alive(time synchronize problem?)')
- raise exception.Invalid(msg)
+ raise exception.Invalid(_("volume node is not alive"
+ "(time synchronize problem?)"))
# Checking src host exists and compute node
src = instance_ref['host']
@@ -139,8 +138,8 @@ class Scheduler(object):
# Checking src host is alive.
if not self.service_is_up(services[0]):
- msg = _('%s is not alive(time synchronize problem?)')
- raise exception.Invalid(msg % src)
+ raise exception.Invalid(_("%s is not alive(time "
+ "synchronize problem?)") % src)
def _live_migration_dest_check(self, context, instance_ref, dest):
"""Live migration check routine (for destination host).
@@ -157,8 +156,8 @@ class Scheduler(object):
# Checking dest host is alive.
if not self.service_is_up(dservice_ref):
- msg = _('%s is not alive(time synchronize problem?)')
- raise exception.Invalid(msg % dest)
+ raise exception.Invalid(_("%s is not alive(time "
+ "synchronize problem?)") % dest)
# Checking whether The host where instance is running
# and dest is not same.
@@ -170,7 +169,9 @@ class Scheduler(object):
% locals())
# Checking dst host still has enough capacities.
- self.has_enough_resources(context, instance_ref, dest)
+ self.assert_compute_node_has_enough_resources(context,
+ instance_ref,
+ dest)
def _live_migration_common_check(self, context, instance_ref, dest):
"""Live migration common check routine.
@@ -202,18 +203,20 @@ class Scheduler(object):
oservice_ref = oservice_refs[0]['compute_service'][0]
# Checking hypervisor is same.
- o = oservice_ref['hypervisor_type']
- d = dservice_ref['hypervisor_type']
- if o != d:
+ orig_hypervisor = oservice_ref['hypervisor_type']
+ dest_hypervisor = dservice_ref['hypervisor_type']
+ if orig_hypervisor != dest_hypervisor:
raise exception.Invalid(_("Different hypervisor type"
- "(%(o)s->%(d)s)')" % locals()))
+ "(%(orig_hypervisor)s->"
+ "%(dest_hypervisor)s)')" % locals()))
# Checkng hypervisor version.
- o = oservice_ref['hypervisor_version']
- d = dservice_ref['hypervisor_version']
- if o > d:
- raise exception.Invalid(_('Older hypervisor version(%(o)s->%(d)s)')
- % locals())
+ orig_hypervisor = oservice_ref['hypervisor_version']
+ dest_hypervisor = dservice_ref['hypervisor_version']
+ if orig_hypervisor > dest_hypervisor:
+ raise exception.Invalid(_("Older hypervisor version"
+ "(%(orig_hypervisor)s->"
+ "%(dest_hypervisor)s)") % locals())
# Checking cpuinfo.
try:
@@ -222,14 +225,15 @@ class Scheduler(object):
{"method": 'compare_cpu',
"args": {'cpu_info': oservice_ref['cpu_info']}})
- except rpc.RemoteError, e:
+ except rpc.RemoteError:
ec2_id = instance_ref['hostname']
src = instance_ref['host']
logging.exception(_("host %(dest)s is not compatible with "
"original host %(src)s.") % locals())
raise
- def has_enough_resources(self, context, instance_ref, dest):
+ def assert_compute_node_has_enough_resources(self, context,
+ instance_ref, dest):
"""Checks if destination host has enough resource for live migration.
Currently, only memory checking has been done.
@@ -276,22 +280,24 @@ class Scheduler(object):
dst_t = db.queue_get_for(context, FLAGS.compute_topic, dest)
src_t = db.queue_get_for(context, FLAGS.compute_topic, src)
- # create tmpfile at dest host
try:
- filename = rpc.call(context, dst_t, {"method": 'mktmpfile'})
- except rpc.RemoteError, e:
- msg = _("Cannot create tmpfile at %s to confirm shared storage.")
- LOG.error(msg % FLAGS.instances_path)
- raise
+ # create tmpfile at dest host
+ filename = rpc.call(context, dst_t,
+ {"method": 'create_shared_storage_test_file'})
- # make sure existence at src host.
- try:
+ # make sure existence at src host.
rpc.call(context, src_t,
- {"method": 'confirm_tmpfile',
+ {"method": 'check_shared_storage_test_file',
"args": {'filename': filename}})
- except (rpc.RemoteError, exception.NotFound), e:
+ except rpc.RemoteError:
ipath = FLAGS.instances_path
- logging.error(_("Cannot comfirm %(ipath)s at %(dest)s is "
- "located at same shared storage.") % locals())
+ logging.error(_("Cannot comfirm tmpfile at %(ipath)s is on "
+ "same shared storage between %(src)s "
+ "and %(dest)s.") % locals())
raise
+
+ finally:
+ rpc.call(context, dst_t,
+ {"method": 'cleanup_shared_storage_test_file',
+ "args": {'filename': filename}})
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index cd5012fd5..a50d3ab20 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -98,24 +98,24 @@ class SchedulerManager(manager.Manager):
# Getting usage resource information
usage = {}
instance_refs = db.instance_get_all_by_host(context,
- compute_ref['host'])
- if 0 == len(instance_refs):
+ compute_ref['host'])
+ if not instance_refs:
return {'resource': resource, 'usage': usage}
project_ids = [i['project_id'] for i in instance_refs]
project_ids = list(set(project_ids))
- for i in project_ids:
+ for project_id in project_ids:
vcpus = db.instance_get_vcpu_sum_by_host_and_project(context,
host,
- i)
+ project_id)
mem = db.instance_get_memory_sum_by_host_and_project(context,
host,
- i)
+ project_id)
hdd = db.instance_get_disk_sum_by_host_and_project(context,
host,
- i)
- usage[i] = {'vcpus': int(vcpus),
- 'memory_mb': int(mem),
- 'local_gb': int(hdd)}
+ project_id)
+ usage[project_id] = {'vcpus': int(vcpus),
+ 'memory_mb': int(mem),
+ 'local_gb': int(hdd)}
return {'resource': resource, 'usage': usage}
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index c4e4d148e..62db42b11 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -661,7 +661,6 @@ class SimpleDriverTestCase(test.TestCase):
self.scheduler.live_migration(self.context, FLAGS.compute_topic,
instance_id=instance_id,
dest=i_ref['host'])
- self.mox.UnsetStubs()
i_ref = db.instance_get(self.context, instance_id)
self.assertTrue(i_ref['state_description'] == 'migrating')
@@ -824,10 +823,15 @@ class SimpleDriverTestCase(test.TestCase):
topic = FLAGS.compute_topic
driver.rpc.call(mox.IgnoreArg(),
db.queue_get_for(self.context, topic, dest),
- {"method": 'mktmpfile'}).AndReturn(fpath)
+ {"method": 'create_shared_storage_test_file'}).AndReturn(fpath)
driver.rpc.call(mox.IgnoreArg(),
db.queue_get_for(mox.IgnoreArg(), topic, i_ref['host']),
- {"method": 'confirm_tmpfile', "args": {'filename': fpath}})
+ {"method": 'check_shared_storage_test_file',
+ "args": {'filename': fpath}})
+ driver.rpc.call(mox.IgnoreArg(),
+ db.queue_get_for(mox.IgnoreArg(), topic, dest),
+ {"method": 'cleanup_shared_storage_test_file',
+ "args": {'filename': fpath}})
self.mox.ReplayAll()
try:
@@ -838,7 +842,6 @@ class SimpleDriverTestCase(test.TestCase):
c = (e.message.find('does not exist') >= 0)
self.assertTrue(c)
- self.mox.UnsetStubs()
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
@@ -867,7 +870,6 @@ class SimpleDriverTestCase(test.TestCase):
c = (e.message.find(_('Different hypervisor type')) >= 0)
self.assertTrue(c)
- self.mox.UnsetStubs()
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
db.service_destroy(self.context, s_ref2['id'])
@@ -898,7 +900,6 @@ class SimpleDriverTestCase(test.TestCase):
c = (e.message.find(_('Older hypervisor version')) >= 0)
self.assertTrue(c)
- self.mox.UnsetStubs()
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
db.service_destroy(self.context, s_ref2['id'])
@@ -934,7 +935,6 @@ class SimpleDriverTestCase(test.TestCase):
c = (e.message.find(_("doesn't have compatibility to")) >= 0)
self.assertTrue(c)
- self.mox.UnsetStubs()
db.instance_destroy(self.context, instance_id)
db.service_destroy(self.context, s_ref['id'])
db.service_destroy(self.context, s_ref2['id'])
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index f46b5950e..17b80c294 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -14,7 +14,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-import libvirt
import mox
from xml.etree.ElementTree import fromstring as xml_to_tree
@@ -60,6 +59,7 @@ class LibvirtConnTestCase(test.TestCase):
admin=True)
self.project = self.manager.create_project('fake', 'fake', 'fake')
self.network = utils.import_object(FLAGS.network_manager)
+ self.context = context.get_admin_context()
FLAGS.instances_path = ''
self.call_libvirt_dependant_setup = False
@@ -73,22 +73,52 @@ class LibvirtConnTestCase(test.TestCase):
'bridge': 'br101',
'instance_type': 'm1.small'}
- def libvirt_dependant_setup(self):
- """A setup method of LibvirtConnection dependent test."""
- # try to connect libvirt. if fail, skip test.
- self.call_libvirt_dependant_setup = True
- try:
- libvirt.openReadOnly('qemu:///system')
- except libvirt.libvirtError:
- return
- return libvirt_conn.get_connection(False)
-
- def libvirt_dependant_teardown(self):
- """teardown method of LibvirtConnection dependent test."""
- if self.call_libvirt_dependant_setup:
- libvirt_conn.libvirt = None
- libvirt_conn.libxml2 = None
- self.call_libvirt_dependant_setup = False
+ def create_fake_libvirt_mock(self, **kwargs):
+ """Defining mocks for LibvirtConnection(libvirt is not used)."""
+
+ # A fake libvirt.virtConnect
+ class FakeLibvirtConnection(object):
+ def getVersion(self):
+ return 12003
+
+ def getType(self):
+ return 'qemu'
+
+ def getCapabilities(self):
+ return 'qemu'
+
+ def listDomainsID(self):
+ return []
+
+ def getCapabilitied(self):
+ return
+
+ # A fake libvirt_conn.IptablesFirewallDriver
+ class FakeIptablesFirewallDriver(object):
+ def __init__(self, **kwargs):
+ pass
+
+ # Creating mocks
+ fake = FakeLibvirtConnection()
+ fakeip = FakeIptablesFirewallDriver
+ # Customizing above fake if necessary
+ for key, val in kwargs.items():
+ fake.__setattr__(key, val)
+
+ # Inevitable mocks for libvirt_conn.LibvirtConnection
+ self.mox.StubOutWithMock(libvirt_conn.utils, 'import_class')
+ libvirt_conn.utils.import_class(mox.IgnoreArg()).AndReturn(fakeip)
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn')
+ libvirt_conn.LibvirtConnection._conn = fake
+
+ def create_service(self, **kwargs):
+ service_ref = {'host': kwargs.get('host', 'dummy'),
+ 'binary': 'nova-compute',
+ 'topic': 'compute',
+ 'report_count': 0,
+ 'availability_zone': 'zone'}
+
+ return db.service_create(context.get_admin_context(), service_ref)
def test_xml_and_uri_no_ramdisk_no_kernel(self):
instance_data = dict(self.test_instance)
@@ -244,306 +274,55 @@ class LibvirtConnTestCase(test.TestCase):
self.assertEquals(uri, testuri)
db.instance_destroy(user_context, instance_ref['id'])
- def test_get_vcpu_used(self):
- """Check if get_local_gb_total returns appropriate disk value."""
- self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn')
- libvirt_conn.LibvirtConnection._conn.listDomainsID().AndReturn([1, 2])
- vdmock = self.mox.CreateMock(libvirt.virDomain)
- self.mox.StubOutWithMock(vdmock, "vcpus")
- vdmock.vcpus().AndReturn(['', [('dummycpu'), ('dummycpu')]])
- vdmock.vcpus().AndReturn(['', [('dummycpu'), ('dummycpu')]])
- arg = mox.IgnoreArg()
- libvirt_conn.LibvirtConnection._conn.lookupByID(arg).AndReturn(vdmock)
- libvirt_conn.LibvirtConnection._conn.lookupByID(arg).AndReturn(vdmock)
-
- self.mox.ReplayAll()
- conn = libvirt_conn.LibvirtConnection(False)
- self.assertTrue(conn.get_vcpu_used() == 4)
-
- def test_get_cpu_info_inappropreate_xml(self):
- """Raise exception if given xml is inappropriate."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
-
- xml = """
- x86_64
- Nehalem
- Intel
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- """
-
- self.mox.StubOutWithMock(conn._conn, 'getCapabilities')
- conn._conn.getCapabilities().AndReturn(xml)
-
- self.mox.ReplayAll()
- try:
- conn.get_cpu_info()
- except exception.Invalid, e:
- c1 = (0 <= e.message.find('Invalid xml'))
- self.assertTrue(c1)
-
- def test_get_cpu_info_inappropreate_xml2(self):
- """Raise exception if given xml is inappropriate(topology tag)."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
-
- xml = """
- x86_64
- Nehalem
- Intel
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- """
- self.mox.StubOutWithMock(conn._conn, 'getCapabilities')
- conn._conn.getCapabilities().AndReturn(xml)
-
- self.mox.ReplayAll()
- try:
- conn.get_cpu_info()
- except exception.Invalid, e:
- c1 = (0 <= e.message.find('Invalid xml: topology'))
- self.assertTrue(c1)
-
def test_update_available_resource_works_correctly(self):
"""Confirm compute_service table is updated successfully."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
-
- host = 'dummy'
- zone = 'dummyzone'
- ctxt = context.get_admin_context()
org_path = FLAGS.instances_path = ''
FLAGS.instances_path = '.'
- service_ref = db.service_create(ctxt,
- {'host': host,
- 'binary': 'nova-compute',
- 'topic': 'compute',
- 'report_count': 0,
- 'availability_zone': zone})
- conn.update_available_resource(ctxt, host)
+ service_ref = self.create_service(host='dummy')
+ self.create_fake_libvirt_mock()
+ self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection,
+ 'get_cpu_info')
+ libvirt_conn.LibvirtConnection.get_cpu_info().AndReturn('cpuinfo')
- service_ref = db.service_get(ctxt, service_ref['id'])
- print service_ref['compute_service']
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ conn.update_available_resource(self.context, 'dummy')
+ service_ref = db.service_get(self.context, service_ref['id'])
compute_service = service_ref['compute_service'][0]
+
c1 = (compute_service['vcpus'] > 0)
c2 = (compute_service['memory_mb'] > 0)
c3 = (compute_service['local_gb'] > 0)
- # vcpu_used is checked at test_get_vcpu_used.
- c4 = (compute_service['memory_mb_used'] > 0)
- c5 = (compute_service['local_gb_used'] > 0)
- c6 = (len(compute_service['hypervisor_type']) > 0)
- c7 = (compute_service['hypervisor_version'] > 0)
+ c4 = (compute_service['vcpus_used'] == 0)
+ c5 = (compute_service['memory_mb_used'] > 0)
+ c6 = (compute_service['local_gb_used'] > 0)
+ c7 = (len(compute_service['hypervisor_type']) > 0)
+ c8 = (compute_service['hypervisor_version'] > 0)
- self.assertTrue(c1 and c2 and c3 and c4 and c5 and c6 and c7)
+ self.assertTrue(c1 and c2 and c3 and c4 and c5 and c6 and c7 and c8)
- db.service_destroy(ctxt, service_ref['id'])
+ db.service_destroy(self.context, service_ref['id'])
FLAGS.instances_path = org_path
- def test_update_resource_info_raise_exception(self):
+ def test_update_resource_info_no_compute_record_found(self):
"""Raise exception if no recorde found on services table."""
- host = 'dummy'
- org_path = FLAGS.instances_path = ''
- FLAGS.instances_path = '.'
- try:
- conn = libvirt_conn.LibvirtConnection(False)
- conn.update_available_resource(context.get_admin_context(), host)
- except exception.Invalid, e:
- msg = 'Cannot update compute manager specific info'
- c1 = (0 <= e.message.find(msg))
- self.assertTrue(c1)
- FLAGS.instances_path = org_path
-
- def test_compare_cpu_works_correctly(self):
- """Calling libvirt.compute_cpu() and works correctly."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
- host = 'dummy'
- zone = 'dummyzone'
- ctxt = context.get_admin_context()
org_path = FLAGS.instances_path = ''
FLAGS.instances_path = '.'
-
- service_ref = db.service_create(ctxt,
- {'host': host,
- 'binary': 'nova-compute',
- 'topic': 'compute',
- 'report_count': 0,
- 'availability_zone': zone})
- conn.update_available_resource(ctxt, host)
- service_ref = db.service_get(ctxt, service_ref['id'])
- ret = conn.compare_cpu(service_ref['compute_service'][0]['cpu_info'])
- self.assertTrue(ret == None)
-
- db.service_destroy(ctxt, service_ref['id'])
- FLAGS.instances_path = org_path
-
- def test_compare_cpu_no_compatibility(self):
- """Libvirt.compare_cpu() return less than 0.(no compatibility)."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
-
- t = {}
- t['arch'] = 'x86'
- t['model'] = 'model'
- t['vendor'] = 'Intel'
- t['topology'] = {'cores': "2", "threads": "1", "sockets": "4"}
- t['features'] = ["tm"]
- cpu_info = utils.dumps(t)
- self.mox.StubOutWithMock(conn._conn, 'compareCPU')
- conn._conn.compareCPU(mox.IgnoreArg(), 0).AndReturn(0)
-
- self.mox.ReplayAll()
- self.assertRaises(exception.Invalid, conn.compare_cpu, cpu_info)
-
- def test_ensure_filtering_rules_for_instance_works_correctly(self):
- """ensure_filtering_rules_for_instance() works successfully."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
-
- instance_ref = models.Instance()
- instance_ref.__setitem__('id', 1)
- fwdriver = conn.firewall_driver
-
- self.mox.StubOutWithMock(fwdriver, 'setup_basic_filtering')
- fwdriver.setup_basic_filtering(instance_ref)
- self.mox.StubOutWithMock(fwdriver, 'prepare_instance_filter')
- fwdriver.prepare_instance_filter(instance_ref)
- self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn')
- n = 'nova-instance-%s' % instance_ref.name
- conn._conn.nwfilterLookupByName(n)
-
- self.mox.ReplayAll()
- conn.ensure_filtering_rules_for_instance(instance_ref)
-
- def test_ensure_filtering_rules_for_instance_timeout(self):
- """ensure_filtering_fules_for_instance() finishes with timeout."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
-
- instance_ref = models.Instance()
- instance_ref.__setitem__('id', 1)
- fwdriver = conn.firewall_driver
-
- self.mox.StubOutWithMock(fwdriver, 'setup_basic_filtering')
- fwdriver.setup_basic_filtering(instance_ref)
- self.mox.StubOutWithMock(fwdriver, 'prepare_instance_filter')
- fwdriver.prepare_instance_filter(instance_ref)
- self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn')
- n = 'nova-instance-%s' % instance_ref.name
- for i in range(FLAGS.live_migration_retry_count):
- conn._conn.nwfilterLookupByName(n).\
- AndRaise(libvirt.libvirtError('ERR'))
-
- self.mox.ReplayAll()
- try:
- conn.ensure_filtering_rules_for_instance(instance_ref)
- except exception.Error, e:
- c1 = (0 <= e.message.find('Timeout migrating for'))
- self.assertTrue(c1)
-
- def test_live_migration_works_correctly(self):
- """_live_migration() works as expected correctly."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
-
- class dummyCall(object):
- f = None
-
- def start(self, interval=0, now=False):
- pass
-
- i_ref = models.Instance()
- i_ref.__setitem__('id', 1)
- ctxt = context.get_admin_context()
-
- vdmock = self.mox.CreateMock(libvirt.virDomain)
- self.mox.StubOutWithMock(vdmock, "migrateToURI")
- vdmock.migrateToURI(FLAGS.live_migration_uri % 'dest',
- mox.IgnoreArg(),
- None, FLAGS.live_migration_bandwidth).\
- AndReturn(None)
- self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn')
- conn._conn.lookupByName(i_ref.name).AndReturn(vdmock)
- self.mox.StubOutWithMock(libvirt_conn.utils, 'LoopingCall')
- libvirt_conn.utils.LoopingCall(f=None).AndReturn(dummyCall())
+ self.create_fake_libvirt_mock()
self.mox.ReplayAll()
- # Nothing to do with setting post_method/recover_method or not.
- ret = conn._live_migration(ctxt, i_ref, 'dest', '', '')
- self.assertTrue(ret == None)
-
- def test_live_migration_raises_exception(self):
- """Confirms recover method is called when exceptions are raised."""
- conn = self.libvirt_dependant_setup()
- if not conn:
- return
-
- i_ref = models.Instance()
- i_ref.__setitem__('id', 1)
- ctxt = context.get_admin_context()
-
- def dummy_recover_method(c, instance, host=None):
- pass
-
- vdmock = self.mox.CreateMock(libvirt.virDomain)
- self.mox.StubOutWithMock(vdmock, "migrateToURI")
- vdmock.migrateToURI(FLAGS.live_migration_uri % 'dest',
- mox.IgnoreArg(),
- None, FLAGS.live_migration_bandwidth).\
- AndRaise(libvirt.libvirtError('ERR'))
- self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection, '_conn')
- conn._conn.lookupByName(i_ref.name).AndReturn(vdmock)
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertRaises(exception.Invalid,
+ conn.update_available_resource,
+ self.context, 'dummy')
- self.mox.ReplayAll()
- self.assertRaises(libvirt.libvirtError,
- conn._live_migration,
- ctxt, i_ref, 'dest',
- '', dummy_recover_method)
+ FLAGS.instances_path = org_path
def tearDown(self):
self.manager.delete_project(self.project)
self.manager.delete_user(self.user)
super(LibvirtConnTestCase, self).tearDown()
- self.libvirt_dependant_teardown()
class IptablesFirewallTestCase(test.TestCase):
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 75e4f0a53..70fdcc453 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -891,8 +891,8 @@ class LibvirtConnection(object):
"""
total = 0
- for i in self._conn.listDomainsID():
- dom = self._conn.lookupByID(i)
+ for dom_id in self._conn.listDomainsID():
+ dom = self._conn.lookupByID(dom_id)
total += len(dom.vcpus()[1])
return total
@@ -1048,7 +1048,7 @@ class LibvirtConnection(object):
'cpu_info': self.get_cpu_info()}
compute_service_ref = service_ref['compute_service']
- if len(compute_service_ref) == 0:
+ if not compute_service_ref:
LOG.info(_('Compute_service record is created for %s ') % host)
dic['service_id'] = service_ref['id']
db.compute_service_create(ctxt, dic)
@@ -1124,7 +1124,7 @@ class LibvirtConnection(object):
# wait for completion
timeout_count = range(FLAGS.live_migration_retry_count)
- while len(timeout_count) != 0:
+ while not timeout_count:
try:
filter_name = 'nova-instance-%s' % instance_ref.name
self._conn.nwfilterLookupByName(filter_name)
@@ -1198,7 +1198,7 @@ class LibvirtConnection(object):
None,
FLAGS.live_migration_bandwidth)
- except Exception, e:
+ except Exception:
recover_method(ctxt, instance_ref)
raise
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index 5dc9077b4..9dea35b35 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -175,5 +175,5 @@ class VolumeManager(manager.Manager):
def check_for_export(self, context, instance_id):
"""Make sure whether volume is exported."""
instance_ref = self.db.instance_get(context, instance_id)
- for v in instance_ref['volumes']:
- self.driver.check_for_export(context, v['id'])
+ for volume in instance_ref['volumes']:
+ self.driver.check_for_export(context, volume['id'])
--
cgit
From 6797c5acc47fb5111ef821d6b074cb635692a9fb Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Thu, 3 Mar 2011 15:41:45 +0000
Subject: Add in multi-tenant support in openstack api.
---
bin/nova-manage | 3 +
nova/api/openstack/__init__.py | 25 ++++
nova/api/openstack/accounts.py | 73 +++++++++++
nova/api/openstack/auth.py | 54 ++++++--
nova/api/openstack/backup_schedules.py | 6 +-
nova/api/openstack/consoles.py | 10 +-
nova/api/openstack/flavors.py | 6 +-
nova/api/openstack/images.py | 12 +-
nova/api/openstack/servers.py | 38 +++---
nova/api/openstack/shared_ip_groups.py | 12 +-
nova/api/openstack/users.py | 93 ++++++++++++++
nova/api/openstack/zones.py | 12 +-
nova/auth/novarc.template | 2 +-
nova/db/sqlalchemy/api.py | 3 +
nova/tests/api/openstack/fakes.py | 82 +++++++++++-
nova/tests/api/openstack/test_accounts.py | 123 ++++++++++++++++++
nova/tests/api/openstack/test_adminapi.py | 6 +-
nova/tests/api/openstack/test_auth.py | 17 ++-
nova/tests/api/openstack/test_flavors.py | 4 +-
nova/tests/api/openstack/test_images.py | 6 +-
nova/tests/api/openstack/test_servers.py | 51 ++++----
nova/tests/api/openstack/test_users.py | 139 +++++++++++++++++++++
nova/tests/api/openstack/test_zones.py | 12 +-
plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 2 +-
24 files changed, 688 insertions(+), 103 deletions(-)
create mode 100644 nova/api/openstack/accounts.py
create mode 100644 nova/api/openstack/users.py
create mode 100644 nova/tests/api/openstack/test_accounts.py
create mode 100644 nova/tests/api/openstack/test_users.py
diff --git a/bin/nova-manage b/bin/nova-manage
index 89332f2af..8f8f0a6a8 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -433,6 +433,8 @@ class ProjectCommands(object):
"been created.\nPlease create a database by running a "
"nova-api server on this host.")
+AccountCommands = ProjectCommands
+
class FixedIpCommands(object):
"""Class for managing fixed ip."""
@@ -663,6 +665,7 @@ class VolumeCommands(object):
CATEGORIES = [
('user', UserCommands),
+ ('account', AccountCommands),
('project', ProjectCommands),
('role', RoleCommands),
('shell', ShellCommands),
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index b1b38ed2d..73d52192e 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -27,6 +27,7 @@ import webob.exc
from nova import flags
from nova import log as logging
from nova import wsgi
+from nova.api.openstack import accounts
from nova.api.openstack import faults
from nova.api.openstack import backup_schedules
from nova.api.openstack import consoles
@@ -34,6 +35,7 @@ from nova.api.openstack import flavors
from nova.api.openstack import images
from nova.api.openstack import servers
from nova.api.openstack import shared_ip_groups
+from nova.api.openstack import users
from nova.api.openstack import zones
@@ -71,6 +73,18 @@ class APIRouter(wsgi.Router):
def __init__(self):
mapper = routes.Mapper()
+ accounts_controller = accounts.Controller()
+ mapper.connect("account", "/{id}",
+ controller=accounts_controller, action="show",
+ conditions=dict(method=["GET"]))
+ if FLAGS.allow_admin_api:
+ mapper.connect("/{id}",
+ controller=accounts_controller, action="update",
+ conditions=dict(method=["PUT"]))
+ mapper.connect("/{id}",
+ controller=accounts_controller, action="delete",
+ conditions=dict(method=["DELETE"]))
+
server_members = {'action': 'POST'}
if FLAGS.allow_admin_api:
LOG.debug(_("Including admin operations in API."))
@@ -84,27 +98,38 @@ class APIRouter(wsgi.Router):
server_members['inject_network_info'] = 'POST'
mapper.resource("zone", "zones", controller=zones.Controller(),
+ path_prefix="{account_id}/",
+ collection={'detail': 'GET'})
+
+ mapper.resource("user", "users", controller=users.Controller(),
+ path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("server", "servers", controller=servers.Controller(),
collection={'detail': 'GET'},
+ path_prefix="{account_id}/",
member=server_members)
mapper.resource("backup_schedule", "backup_schedule",
controller=backup_schedules.Controller(),
+ path_prefix="{account_id}/servers/{server_id}/",
parent_resource=dict(member_name='server',
collection_name='servers'))
mapper.resource("console", "consoles",
controller=consoles.Controller(),
+ path_prefix="{account_id}/servers/{server_id}/",
parent_resource=dict(member_name='server',
collection_name='servers'))
mapper.resource("image", "images", controller=images.Controller(),
+ path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("flavor", "flavors", controller=flavors.Controller(),
+ path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("shared_ip_group", "shared_ip_groups",
+ path_prefix="{account_id}/",
collection={'detail': 'GET'},
controller=shared_ip_groups.Controller())
diff --git a/nova/api/openstack/accounts.py b/nova/api/openstack/accounts.py
new file mode 100644
index 000000000..264fdab99
--- /dev/null
+++ b/nova/api/openstack/accounts.py
@@ -0,0 +1,73 @@
+# Copyright 2011 OpenStack LLC.
+# 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 common
+
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import wsgi
+
+from nova.auth import manager
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.api.openstack')
+
+
+def _translate_keys(account):
+ return dict(id=account.id,
+ name=account.name,
+ description=account.description,
+ manager=account.project_manager_id)
+
+
+class Controller(wsgi.Controller):
+
+ _serialization_metadata = {
+ 'application/xml': {
+ "attributes": {
+ "account": ["id", "name", "description", "manager"]}}}
+
+ def __init__(self):
+ self.manager = manager.AuthManager()
+
+ def _check_admin(self, context):
+ """ We cannot depend on the db layer to check for admin access
+ for the auth manager, so we do it here """
+ if not context.is_admin:
+ raise exception.NotAuthorized("Not admin user.")
+
+ def show(self, req, id):
+ """Return data about the given account id"""
+ account = self.manager.get_project(id)
+ return dict(account=_translate_keys(account))
+
+ def delete(self, req, id):
+ self._check_admin(req.environ['nova.context'])
+ self.manager.delete_project(id)
+ return {}
+
+ def update(self, req, id):
+ """ This is really create or update. """
+ self._check_admin(req.environ['nova.context'])
+ env = self._deserialize(req.body, req)
+ description = env['account'].get('description')
+ manager = env['account'].get('manager')
+ try:
+ account = self.manager.get_project(id)
+ self.manager.modify_project(id, manager, description)
+ except exception.NotFound:
+ account = self.manager.create_project(id, manager, description)
+ return dict(account=_translate_keys(account))
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 6011e6115..e77910fed 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -28,11 +28,13 @@ from nova import context
from nova import db
from nova import exception
from nova import flags
+from nova import log as logging
from nova import manager
from nova import utils
from nova import wsgi
from nova.api.openstack import faults
+LOG = logging.getLogger('nova.api.openstack')
FLAGS = flags.FLAGS
@@ -50,14 +52,27 @@ class AuthMiddleware(wsgi.Middleware):
def __call__(self, req):
if not self.has_authentication(req):
return self.authenticate(req)
-
user = self.get_user_by_authentication(req)
+ account_name = req.path_info_peek()
if not user:
return faults.Fault(webob.exc.HTTPUnauthorized())
- project = self.auth.get_project(FLAGS.default_project)
- req.environ['nova.context'] = context.RequestContext(user, project)
+ if not account_name:
+ if self.auth.is_admin(user):
+ account_name = FLAGS.default_project
+ else:
+ return faults.Fault(webob.exc.HTTPUnauthorized())
+ try:
+ account = self.auth.get_project(account_name)
+ except exception.NotFound:
+ return faults.Fault(webob.exc.HTTPUnauthorized())
+
+ if not self.auth.is_admin(user) and \
+ not self.auth.is_project_member(user, account):
+ return faults.Fault(webob.exc.HTTPUnauthorized())
+
+ req.environ['nova.context'] = context.RequestContext(user, account)
return self.application
def has_authentication(self, req):
@@ -70,6 +85,7 @@ class AuthMiddleware(wsgi.Middleware):
# Unless the request is explicitly made against // don't
# honor it
path_info = req.path_info
+ account_name = None
if len(path_info) > 1:
return faults.Fault(webob.exc.HTTPUnauthorized())
@@ -79,7 +95,10 @@ class AuthMiddleware(wsgi.Middleware):
except KeyError:
return faults.Fault(webob.exc.HTTPUnauthorized())
- token, user = self._authorize_user(username, key, req)
+ if ':' in username:
+ account_name, username = username.rsplit(':', 1)
+
+ token, user = self._authorize_user(username, account_name, key, req)
if user and token:
res = webob.Response()
res.headers['X-Auth-Token'] = token.token_hash
@@ -116,23 +135,44 @@ class AuthMiddleware(wsgi.Middleware):
return self.auth.get_user(token.user_id)
return None
- def _authorize_user(self, username, key, req):
+ def _authorize_user(self, username, account_name, key, req):
"""Generates a new token and assigns it to a user.
username - string
+ account_name - string
key - string API key
req - webob.Request object
"""
ctxt = context.get_admin_context()
user = self.auth.get_user_from_access_key(key)
+ if account_name:
+ try:
+ account = self.auth.get_project(account_name)
+ except exception.NotFound:
+ return None, None
+ else:
+ # (dragondm) punt and try to determine account.
+ # this is something of a hack, but a user on 1 account is a
+ # common case, and is the way the current RS code works.
+ accounts = self.auth.get_projects(user=user)
+ if len(accounts) == 1:
+ account = accounts[0]
+ else:
+ #we can't tell what account they are logging in for.
+ return None, None
+
if user and user.name == username:
token_hash = hashlib.sha1('%s%s%f' % (username, key,
time.time())).hexdigest()
token_dict = {}
token_dict['token_hash'] = token_hash
token_dict['cdn_management_url'] = ''
- # Same as auth url, e.g. http://foo.org:8774/baz/v1.0
- token_dict['server_management_url'] = req.url
+ # auth url + project (account) id, e.g.
+ # http://foo.org:8774/baz/v1.0/myacct/
+ os_url = '%s%s%s/' % (req.url,
+ '' if req.url.endswith('/') else '/',
+ account.id)
+ token_dict['server_management_url'] = os_url
token_dict['storage_url'] = ''
token_dict['user_id'] = user.id
token = self.db.auth_token_create(ctxt, token_dict)
diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py
index 7abb5f884..a4d5939df 100644
--- a/nova/api/openstack/backup_schedules.py
+++ b/nova/api/openstack/backup_schedules.py
@@ -40,15 +40,15 @@ class Controller(wsgi.Controller):
def __init__(self):
pass
- def index(self, req, server_id):
+ def index(self, req, server_id, **kw):
""" Returns the list of backup schedules for a given instance """
return _translate_keys({})
- def create(self, req, server_id):
+ def create(self, req, server_id, **kw):
""" No actual update method required, since the existing API allows
both create and update through a POST """
return faults.Fault(exc.HTTPNotImplemented())
- def delete(self, req, server_id, id):
+ def delete(self, req, server_id, id, **kw):
""" Deletes an existing backup schedule """
return faults.Fault(exc.HTTPNotImplemented())
diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py
index 9ebdbe710..85b2a4140 100644
--- a/nova/api/openstack/consoles.py
+++ b/nova/api/openstack/consoles.py
@@ -55,7 +55,7 @@ class Controller(wsgi.Controller):
self.console_api = console.API()
super(Controller, self).__init__()
- def index(self, req, server_id):
+ def index(self, req, server_id, **kw):
"""Returns a list of consoles for this instance"""
consoles = self.console_api.get_consoles(
req.environ['nova.context'],
@@ -63,14 +63,14 @@ class Controller(wsgi.Controller):
return dict(consoles=[_translate_keys(console)
for console in consoles])
- def create(self, req, server_id):
+ def create(self, req, server_id, **kw):
"""Creates a new console"""
#info = self._deserialize(req.body, req)
self.console_api.create_console(
req.environ['nova.context'],
int(server_id))
- def show(self, req, server_id, id):
+ def show(self, req, server_id, id, **kw):
"""Shows in-depth information on a specific console"""
try:
console = self.console_api.get_console(
@@ -81,11 +81,11 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return _translate_detail_keys(console)
- def update(self, req, server_id, id):
+ def update(self, req, server_id, id, **kw):
"""You can't update a console"""
raise faults.Fault(exc.HTTPNotImplemented())
- def delete(self, req, server_id, id):
+ def delete(self, req, server_id, id, **kw):
"""Deletes a console"""
try:
self.console_api.delete_console(req.environ['nova.context'],
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index f620d4107..79c3e1ab3 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -32,18 +32,18 @@ class Controller(wsgi.Controller):
"attributes": {
"flavor": ["id", "name", "ram", "disk"]}}}
- def index(self, req):
+ def index(self, req, **kw):
"""Return all flavors in brief."""
return dict(flavors=[dict(id=flavor['id'], name=flavor['name'])
for flavor in self.detail(req)['flavors']])
- def detail(self, req):
+ def detail(self, req, **kw):
"""Return all flavors in detail."""
items = [self.show(req, id)['flavor'] for id in self._all_ids()]
items = common.limited(items, req)
return dict(flavors=items)
- def show(self, req, id):
+ def show(self, req, id, **kw):
"""Return data about the given flavor id."""
for name, val in instance_types.INSTANCE_TYPES.iteritems():
if val['flavorid'] == int(id):
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index cf85a496f..5bc5b9978 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -115,14 +115,14 @@ class Controller(wsgi.Controller):
def __init__(self):
self._service = utils.import_object(FLAGS.image_service)
- def index(self, req):
+ def index(self, req, **kw):
"""Return all public images in brief"""
items = self._service.index(req.environ['nova.context'])
items = common.limited(items, req)
items = [_filter_keys(item, ('id', 'name')) for item in items]
return dict(images=items)
- def detail(self, req):
+ def detail(self, req, **kw):
"""Return all public images in detail"""
try:
items = self._service.detail(req.environ['nova.context'])
@@ -136,7 +136,7 @@ class Controller(wsgi.Controller):
items = [_translate_status(item) for item in items]
return dict(images=items)
- def show(self, req, id):
+ def show(self, req, id, **kw):
"""Return data about the given image id"""
image_id = common.get_image_id_from_image_hash(self._service,
req.environ['nova.context'], id)
@@ -145,11 +145,11 @@ class Controller(wsgi.Controller):
_convert_image_id_to_hash(image)
return dict(image=image)
- def delete(self, req, id):
+ def delete(self, req, id, **kw):
# Only public images are supported for now.
raise faults.Fault(exc.HTTPNotFound())
- def create(self, req):
+ def create(self, req, **kw):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
instance_id = env["image"]["serverId"]
@@ -160,7 +160,7 @@ class Controller(wsgi.Controller):
return dict(image=image_meta)
- def update(self, req, id):
+ def update(self, req, id, **kw):
# Users may not modify public images, and that's all that
# we support for now.
raise faults.Fault(exc.HTTPNotFound())
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 69273ad7b..426de92be 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -105,11 +105,11 @@ class Controller(wsgi.Controller):
self._image_service = utils.import_object(FLAGS.image_service)
super(Controller, self).__init__()
- def index(self, req):
+ def index(self, req, **kw):
""" Returns a list of server names and ids for a given user """
return self._items(req, entity_maker=_translate_keys)
- def detail(self, req):
+ def detail(self, req, **kw):
""" Returns a list of server details for a given user """
return self._items(req, entity_maker=_translate_detail_keys)
@@ -123,7 +123,7 @@ class Controller(wsgi.Controller):
res = [entity_maker(inst)['server'] for inst in limited_list]
return dict(servers=res)
- def show(self, req, id):
+ def show(self, req, id, **kw):
""" Returns server details by server id """
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
@@ -131,7 +131,7 @@ class Controller(wsgi.Controller):
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
- def delete(self, req, id):
+ def delete(self, req, id, **kw):
""" Destroys a server """
try:
self.compute_api.delete(req.environ['nova.context'], id)
@@ -139,7 +139,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
- def create(self, req):
+ def create(self, req, **kw):
""" Creates a new server for a given user """
env = self._deserialize(req.body, req)
if not env:
@@ -180,7 +180,7 @@ class Controller(wsgi.Controller):
onset_files=env.get('onset_files', []))
return _translate_keys(instances[0])
- def update(self, req, id):
+ def update(self, req, id, **kw):
""" Updates the server name or password """
inst_dict = self._deserialize(req.body, req)
if not inst_dict:
@@ -202,7 +202,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPNoContent()
- def action(self, req, id):
+ def action(self, req, id, **kw):
""" Multi-purpose method used to reboot, rebuild, and
resize a server """
input_dict = self._deserialize(req.body, req)
@@ -219,7 +219,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def lock(self, req, id):
+ def lock(self, req, id, **kw):
"""
lock the instance with id
admin only operation
@@ -234,7 +234,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def unlock(self, req, id):
+ def unlock(self, req, id, **kw):
"""
unlock the instance with id
admin only operation
@@ -249,7 +249,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def get_lock(self, req, id):
+ def get_lock(self, req, id, **kw):
"""
return the boolean state of (instance with id)'s lock
@@ -263,7 +263,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def reset_network(self, req, id):
+ def reset_network(self, req, id, **kw):
"""
Reset networking on an instance (admin only).
@@ -277,7 +277,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def inject_network_info(self, req, id):
+ def inject_network_info(self, req, id, **kw):
"""
Inject network info for an instance (admin only).
@@ -291,7 +291,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def pause(self, req, id):
+ def pause(self, req, id, **kw):
""" Permit Admins to Pause the server. """
ctxt = req.environ['nova.context']
try:
@@ -302,7 +302,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def unpause(self, req, id):
+ def unpause(self, req, id, **kw):
""" Permit Admins to Unpause the server. """
ctxt = req.environ['nova.context']
try:
@@ -313,7 +313,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def suspend(self, req, id):
+ def suspend(self, req, id, **kw):
"""permit admins to suspend the server"""
context = req.environ['nova.context']
try:
@@ -324,7 +324,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def resume(self, req, id):
+ def resume(self, req, id, **kw):
"""permit admins to resume the server from suspend"""
context = req.environ['nova.context']
try:
@@ -335,7 +335,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def get_ajax_console(self, req, id):
+ def get_ajax_console(self, req, id, **kw):
""" Returns a url to an instance's ajaxterm console. """
try:
self.compute_api.get_ajax_console(req.environ['nova.context'],
@@ -344,12 +344,12 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
- def diagnostics(self, req, id):
+ def diagnostics(self, req, id, **kw):
"""Permit Admins to retrieve server diagnostics."""
ctxt = req.environ["nova.context"]
return self.compute_api.get_diagnostics(ctxt, id)
- def actions(self, req, id):
+ def actions(self, req, id, **kw):
"""Permit Admins to retrieve server actions."""
ctxt = req.environ["nova.context"]
items = self.compute_api.get_actions(ctxt, id)
diff --git a/nova/api/openstack/shared_ip_groups.py b/nova/api/openstack/shared_ip_groups.py
index 5d78f9377..e3c917749 100644
--- a/nova/api/openstack/shared_ip_groups.py
+++ b/nova/api/openstack/shared_ip_groups.py
@@ -40,26 +40,26 @@ class Controller(wsgi.Controller):
'attributes': {
'sharedIpGroup': []}}}
- def index(self, req):
+ def index(self, req, **kw):
""" Returns a list of Shared IP Groups for the user """
return dict(sharedIpGroups=[])
- def show(self, req, id):
+ def show(self, req, id, **kw):
""" Shows in-depth information on a specific Shared IP Group """
return _translate_keys({})
- def update(self, req, id):
+ def update(self, req, id, **kw):
""" You can't update a Shared IP Group """
raise faults.Fault(exc.HTTPNotImplemented())
- def delete(self, req, id):
+ def delete(self, req, id, **kw):
""" Deletes a Shared IP Group """
raise faults.Fault(exc.HTTPNotImplemented())
- def detail(self, req):
+ def detail(self, req, **kw):
""" Returns a complete list of Shared IP Groups """
return _translate_detail_keys({})
- def create(self, req):
+ def create(self, req, **kw):
""" Creates a new Shared IP group """
raise faults.Fault(exc.HTTPNotImplemented())
diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py
new file mode 100644
index 000000000..c0b7544f9
--- /dev/null
+++ b/nova/api/openstack/users.py
@@ -0,0 +1,93 @@
+# Copyright 2011 OpenStack LLC.
+# 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 common
+
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import wsgi
+
+from nova.auth import manager
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.api.openstack')
+
+
+def _translate_keys(user):
+ return dict(id=user.id,
+ name=user.name,
+ access=user.access,
+ secret=user.secret,
+ admin=user.admin)
+
+
+class Controller(wsgi.Controller):
+
+ _serialization_metadata = {
+ 'application/xml': {
+ "attributes": {
+ "user": ["id", "name", "access", "secret", "admin"]}}}
+
+ def __init__(self):
+ self.manager = manager.AuthManager()
+
+ def _check_admin(self, context):
+ """ We cannot depend on the db layer to check for admin access
+ for the auth manager, so we do it here """
+ if not context.is_admin:
+ raise exception.NotAuthorized("Not admin user")
+
+ def index(self, req, **kw):
+ """Return all users in brief"""
+ users = self.manager.get_users()
+ users = common.limited(users, req)
+ users = [_translate_keys(user) for user in users]
+ return dict(users=users)
+
+ def detail(self, req, **kw):
+ """Return all users in detail"""
+ return self.index(req)
+
+ def show(self, req, id, **kw):
+ """Return data about the given user id"""
+ user = self.manager.get_user(id)
+ return dict(user=_translate_keys(user))
+
+ def delete(self, req, id, **kw):
+ self._check_admin(req.environ['nova.context'])
+ self.manager.delete_user(id)
+ return {}
+
+ def create(self, req, **kw):
+ self._check_admin(req.environ['nova.context'])
+ env = self._deserialize(req.body, req)
+ is_admin = env['user'].get('admin') in ('T', 'True', True)
+ name = env['user'].get('name')
+ access = env['user'].get('access')
+ secret = env['user'].get('secret')
+ user = self.manager.create_user(name, access, secret, is_admin)
+ return dict(user=_translate_keys(user))
+
+ def update(self, req, id, **kw):
+ self._check_admin(req.environ['nova.context'])
+ env = self._deserialize(req.body, req)
+ is_admin = env['user'].get('admin')
+ if is_admin is not None:
+ is_admin = is_admin in ('T', 'True', True)
+ access = env['user'].get('access')
+ secret = env['user'].get('secret')
+ self.manager.modify_user(id, access, secret, is_admin)
+ return dict(user=_translate_keys(self.manager.get_user(id)))
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
index d5206da20..30bf2b67b 100644
--- a/nova/api/openstack/zones.py
+++ b/nova/api/openstack/zones.py
@@ -43,35 +43,35 @@ class Controller(wsgi.Controller):
"attributes": {
"zone": ["id", "api_url"]}}}
- def index(self, req):
+ def index(self, req, **kw):
"""Return all zones in brief"""
items = db.zone_get_all(req.environ['nova.context'])
items = common.limited(items, req)
items = [_scrub_zone(item) for item in items]
return dict(zones=items)
- def detail(self, req):
+ def detail(self, req, **kw):
"""Return all zones in detail"""
return self.index(req)
- def show(self, req, id):
+ def show(self, req, id, **kw):
"""Return data about the given zone id"""
zone_id = int(id)
zone = db.zone_get(req.environ['nova.context'], zone_id)
return dict(zone=_scrub_zone(zone))
- def delete(self, req, id):
+ def delete(self, req, id, **kw):
zone_id = int(id)
db.zone_delete(req.environ['nova.context'], zone_id)
return {}
- def create(self, req):
+ def create(self, req, **kw):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
zone = db.zone_create(context, env["zone"])
return dict(zone=_scrub_zone(zone))
- def update(self, req, id):
+ def update(self, req, id, **kw):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
zone_id = int(id)
diff --git a/nova/auth/novarc.template b/nova/auth/novarc.template
index cda2ecc28..1c917ad44 100644
--- a/nova/auth/novarc.template
+++ b/nova/auth/novarc.template
@@ -11,5 +11,5 @@ export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this se
alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}"
alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}"
export NOVA_API_KEY="%(access)s"
-export NOVA_USERNAME="%(user)s"
+export NOVA_USERNAME="%(project)s:%(user)s"
export NOVA_URL="%(os)s"
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 6df2a8843..e311f310a 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1861,8 +1861,11 @@ def project_get_by_user(context, user_id):
session = get_session()
user = session.query(models.User).\
filter_by(deleted=can_read_deleted(context)).\
+ filter_by(id=user_id).\
options(joinedload_all('projects')).\
first()
+ if not user:
+ raise exception.NotFound(_('Invalid user_id %s') % user_id)
return user.projects
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 49ce8c1b5..03b26e29a 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -26,7 +26,6 @@ from paste import urlmap
from glance import client as glance_client
-from nova import auth
from nova import context
from nova import exception as exc
from nova import flags
@@ -35,6 +34,7 @@ import nova.api.openstack.auth
from nova.api import openstack
from nova.api.openstack import auth
from nova.api.openstack import ratelimiting
+from nova.auth.manager import User, Project
from nova.image import glance
from nova.image import local
from nova.image import service
@@ -227,19 +227,97 @@ class FakeAuthDatabase(object):
class FakeAuthManager(object):
auth_data = {}
+ projects = {}
+
+ @classmethod
+ def clear_fakes(cls):
+ cls.auth_data = {}
+ cls.projects = {}
+
+ @classmethod
+ def reset_fake_data(cls):
+ cls.auth_data = dict(acc1=User('guy1', 'guy1', 'acc1',
+ 'fortytwo!', False))
+ cls.projects = dict(testacct=Project('testacct',
+ 'testacct',
+ 'guy1',
+ 'test',
+ []))
def add_user(self, key, user):
FakeAuthManager.auth_data[key] = user
+ def get_users(self):
+ return FakeAuthManager.auth_data.values()
+
def get_user(self, uid):
for k, v in FakeAuthManager.auth_data.iteritems():
if v.id == uid:
return v
return None
- def get_project(self, pid):
+ def delete_user(self, uid):
+ for k, v in FakeAuthManager.auth_data.items():
+ if v.id == uid:
+ del FakeAuthManager.auth_data[k]
return None
+ def create_user(self, name, access=None, secret=None, admin=False):
+ u = User(name, name, access, secret, admin)
+ FakeAuthManager.auth_data[access] = u
+ return u
+
+ def modify_user(self, user_id, access=None, secret=None, admin=None):
+ user = None
+ for k, v in FakeAuthManager.auth_data.iteritems():
+ if v.id == user_id:
+ user = v
+ if user:
+ user.access = access
+ user.secret = secret
+ if admin is not None:
+ user.admin = admin
+
+ def is_admin(self, user):
+ return user.admin
+
+ def is_project_member(self, user, project):
+ return ((user.id in project.member_ids) or
+ (user.id == project.project_manager_id))
+
+ def create_project(self, name, manager_user, description=None,
+ member_users=None):
+ member_ids = [User.safe_id(m) for m in member_users] \
+ if member_users else []
+ p = Project(name, name, User.safe_id(manager_user),
+ description, member_ids)
+ FakeAuthManager.projects[name] = p
+ return p
+
+ def delete_project(self, pid):
+ if pid in FakeAuthManager.projects:
+ del FakeAuthManager.projects[pid]
+
+ def modify_project(self, project, manager_user=None, description=None):
+ p = FakeAuthManager.projects.get(project)
+ p.project_manager_id = User.safe_id(manager_user)
+ p.description = description
+
+ def get_project(self, pid):
+ p = FakeAuthManager.projects.get(pid)
+ if p:
+ return p
+ else:
+ raise exc.NotFound
+
+ def get_projects(self, user=None):
+ if not user:
+ return FakeAuthManager.projects.values()
+ else:
+ return [p for p in FakeAuthManager.projects.values()
+ if (user.id in p.member_ids) or
+ (user.id == p.project_manager_id)]
+
def get_user_from_access_key(self, key):
return FakeAuthManager.auth_data.get(key, None)
diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py
new file mode 100644
index 000000000..b2e89824a
--- /dev/null
+++ b/nova/tests/api/openstack/test_accounts.py
@@ -0,0 +1,123 @@
+# Copyright 2010 OpenStack LLC.
+# 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 stubout
+import webob
+import json
+
+import nova.api
+import nova.api.openstack.auth
+from nova import context
+from nova import flags
+from nova import test
+from nova.auth.manager import User
+from nova.tests.api.openstack import fakes
+
+
+FLAGS = flags.FLAGS
+FLAGS.verbose = True
+
+
+def fake_init(self):
+ self.manager = fakes.FakeAuthManager()
+
+
+def fake_admin_check(self, req):
+ return True
+
+
+class AccountsTest(test.TestCase):
+ def setUp(self):
+ super(AccountsTest, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(nova.api.openstack.accounts.Controller, '__init__',
+ fake_init)
+ self.stubs.Set(nova.api.openstack.accounts.Controller, '_check_admin',
+ fake_admin_check)
+ fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.projects = {}
+ fakes.FakeAuthDatabase.data = {}
+ fakes.stub_out_networking(self.stubs)
+ fakes.stub_out_rate_limiting(self.stubs)
+ fakes.stub_out_auth(self.stubs)
+
+ self.allow_admin = FLAGS.allow_admin_api
+ FLAGS.allow_admin_api = True
+ fakemgr = fakes.FakeAuthManager()
+ joeuser = User('guy1', 'guy1', 'acc1', 'fortytwo!', False)
+ superuser = User('guy2', 'guy2', 'acc2', 'swordfish', True)
+ fakemgr.add_user(joeuser.access, joeuser)
+ fakemgr.add_user(superuser.access, superuser)
+ fakemgr.create_project('test1', joeuser)
+ fakemgr.create_project('test2', superuser)
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ FLAGS.allow_admin_api = self.allow_admin
+ super(AccountsTest, self).tearDown()
+
+ def test_get_account(self):
+ req = webob.Request.blank('/v1.0/test1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res_dict['account']['id'], 'test1')
+ self.assertEqual(res_dict['account']['name'], 'test1')
+ self.assertEqual(res_dict['account']['manager'], 'guy1')
+ self.assertEqual(res.status_int, 200)
+
+ def test_account_delete(self):
+ req = webob.Request.blank('/v1.0/test1')
+ req.method = 'DELETE'
+ res = req.get_response(fakes.wsgi_app())
+ self.assertTrue('test1' not in fakes.FakeAuthManager.projects)
+ self.assertEqual(res.status_int, 200)
+
+ def test_account_create(self):
+ body = dict(account=dict(description='test account',
+ manager='guy1'))
+ req = webob.Request.blank('/v1.0/newacct')
+ req.method = 'PUT'
+ req.body = json.dumps(body)
+
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res_dict['account']['id'], 'newacct')
+ self.assertEqual(res_dict['account']['name'], 'newacct')
+ self.assertEqual(res_dict['account']['description'], 'test account')
+ self.assertEqual(res_dict['account']['manager'], 'guy1')
+ self.assertTrue('newacct' in
+ fakes.FakeAuthManager.projects)
+ self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3)
+
+ def test_account_update(self):
+ body = dict(account=dict(description='test account',
+ manager='guy2'))
+ req = webob.Request.blank('/v1.0/test1')
+ req.method = 'PUT'
+ req.body = json.dumps(body)
+
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res_dict['account']['id'], 'test1')
+ self.assertEqual(res_dict['account']['name'], 'test1')
+ self.assertEqual(res_dict['account']['description'], 'test account')
+ self.assertEqual(res_dict['account']['manager'], 'guy2')
+ self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2)
diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py
index dfce1b127..7cb9e8450 100644
--- a/nova/tests/api/openstack/test_adminapi.py
+++ b/nova/tests/api/openstack/test_adminapi.py
@@ -35,7 +35,7 @@ class AdminAPITest(test.TestCase):
def setUp(self):
super(AdminAPITest, self).setUp()
self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -50,7 +50,7 @@ class AdminAPITest(test.TestCase):
def test_admin_enabled(self):
FLAGS.allow_admin_api = True
# We should still be able to access public operations.
- req = webob.Request.blank('/v1.0/flavors')
+ req = webob.Request.blank('/v1.0/testacct/flavors')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
# TODO: Confirm admin operations are available.
@@ -58,7 +58,7 @@ class AdminAPITest(test.TestCase):
def test_admin_disabled(self):
FLAGS.allow_admin_api = False
# We should still be able to access public operations.
- req = webob.Request.blank('/v1.0/flavors')
+ req = webob.Request.blank('/v1.0/testacct/flavors')
res = req.get_response(fakes.wsgi_app())
# TODO: Confirm admin operations are unavailable.
self.assertEqual(res.status_int, 200)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index ff8d42a14..8268a6fb9 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -51,7 +51,9 @@ class Test(test.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
- f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
+ u = nova.auth.manager.User(1, 'herp', None, None, None)
+ f.add_user('derp', u)
+ f.create_project('test', u)
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'herp'
@@ -65,7 +67,9 @@ class Test(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
+ u = nova.auth.manager.User(1, 'herp', None, None, None)
+ f.add_user('derp', u)
+ f.create_project('test', u)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'herp'
@@ -74,7 +78,7 @@ class Test(test.TestCase):
self.assertEqual(result.status, '204 No Content')
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
self.assertEqual(result.headers['X-Server-Management-Url'],
- "http://foo/v1.0/")
+ "http://foo/v1.0/test/")
self.assertEqual(result.headers['X-CDN-Management-Url'],
"")
self.assertEqual(result.headers['X-Storage-Url'], "")
@@ -82,7 +86,7 @@ class Test(test.TestCase):
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouter',
fakes.FakeRouter)
- req = webob.Request.blank('/v1.0/fake')
+ req = webob.Request.blank('/v1.0/test/fake')
req.headers['X-Auth-Token'] = token
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '200 OK')
@@ -176,6 +180,9 @@ class TestLimiter(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
+ u = nova.auth.manager.User(1, 'herp', None, None, None)
+ f.add_user('derp', u)
+ f.create_project('test', u)
f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
req = webob.Request.blank('/v1.0/')
@@ -187,7 +194,7 @@ class TestLimiter(test.TestCase):
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouter',
fakes.FakeRouter)
- req = webob.Request.blank('/v1.0/fake')
+ req = webob.Request.blank('/v1.0/test/fake')
req.method = 'POST'
req.headers['X-Auth-Token'] = token
result = req.get_response(fakes.wsgi_app())
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index 761265965..370dc007c 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -28,7 +28,7 @@ class FlavorsTest(test.TestCase):
def setUp(self):
super(FlavorsTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -39,7 +39,7 @@ class FlavorsTest(test.TestCase):
super(FlavorsTest, self).tearDown()
def test_get_flavor_list(self):
- req = webob.Request.blank('/v1.0/flavors')
+ req = webob.Request.blank('/v1.0/testacct/flavors')
res = req.get_response(fakes.wsgi_app())
def test_get_flavor_by_id(self):
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index e232bc3d5..819ca001e 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -202,7 +202,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
self.orig_image_service = FLAGS.image_service
FLAGS.image_service = 'nova.image.glance.GlanceImageService'
self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -216,7 +216,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
super(ImageControllerWithGlanceServiceTest, self).tearDown()
def test_get_image_index(self):
- req = webob.Request.blank('/v1.0/images')
+ req = webob.Request.blank('/v1.0/testacct/images')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -228,7 +228,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
"image %s not in fixture index!" % str(image))
def test_get_image_details(self):
- req = webob.Request.blank('/v1.0/images/detail')
+ req = webob.Request.blank('/v1.0/testacct/images/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 78beb7df9..d592e06b0 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -118,7 +118,7 @@ class ServersTest(test.TestCase):
def setUp(self):
super(ServersTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -150,7 +150,7 @@ class ServersTest(test.TestCase):
super(ServersTest, self).tearDown()
def test_get_server_by_id(self):
- req = webob.Request.blank('/v1.0/servers/1')
+ req = webob.Request.blank('/v1.0/testacct/servers/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['server']['id'], '1')
@@ -161,7 +161,7 @@ class ServersTest(test.TestCase):
public = ["1.2.3.4"]
new_return_server = return_server_with_addresses(private, public)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
- req = webob.Request.blank('/v1.0/servers/1')
+ req = webob.Request.blank('/v1.0/testacct/servers/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['server']['id'], '1')
@@ -173,7 +173,7 @@ class ServersTest(test.TestCase):
self.assertEqual(addresses["private"][0], private)
def test_get_server_list(self):
- req = webob.Request.blank('/v1.0/servers')
+ req = webob.Request.blank('/v1.0/testacct/servers')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -224,7 +224,7 @@ class ServersTest(test.TestCase):
name='server_test', imageId=2, flavorId=2,
metadata={'hello': 'world', 'open': 'stack'},
personality={}))
- req = webob.Request.blank('/v1.0/servers')
+ req = webob.Request.blank('/v1.0/testacct/servers')
req.method = 'POST'
req.body = json.dumps(body)
@@ -233,7 +233,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_update_no_body(self):
- req = webob.Request.blank('/v1.0/servers/1')
+ req = webob.Request.blank('/v1.0/testacct/servers/1')
req.method = 'PUT'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 422)
@@ -251,7 +251,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
- req = webob.Request.blank('/v1.0/servers/1')
+ req = webob.Request.blank('/v1.0/testacct/servers/1')
req.method = 'PUT'
req.body = self.body
req.get_response(fakes.wsgi_app())
@@ -267,30 +267,30 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
- req = webob.Request.blank('/v1.0/servers/1')
+ req = webob.Request.blank('/v1.0/testacct/servers/1')
req.method = 'PUT'
req.body = self.body
req.get_response(fakes.wsgi_app())
def test_create_backup_schedules(self):
- req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
req.method = 'POST'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_delete_backup_schedules(self):
- req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_get_server_backup_schedules(self):
- req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_get_all_server_details(self):
- req = webob.Request.blank('/v1.0/servers/detail')
+ req = webob.Request.blank('/v1.0/testacct/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -321,7 +321,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
return_servers_with_host)
- req = webob.Request.blank('/v1.0/servers/detail')
+ req = webob.Request.blank('/v1.0/testacct/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -341,7 +341,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/pause')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/pause')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -353,7 +353,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/unpause')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/unpause')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -365,7 +365,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/suspend')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/suspend')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -377,7 +377,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/resume')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/resume')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -389,7 +389,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/reset_network')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/reset_network')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -401,7 +401,8 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/inject_network_info')
+ req = webob.Request.blank(
+ '/v1.0/testacct/servers/1/inject_network_info')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -409,13 +410,13 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_server_diagnostics(self):
- req = webob.Request.blank("/v1.0/servers/1/diagnostics")
+ req = webob.Request.blank("/v1.0/testacct/servers/1/diagnostics")
req.method = "GET"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
def test_server_actions(self):
- req = webob.Request.blank("/v1.0/servers/1/actions")
+ req = webob.Request.blank("/v1.0/testacct/servers/1/actions")
req.method = "GET"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
@@ -424,7 +425,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/action')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -434,7 +435,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/action')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -444,14 +445,14 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/servers/1/action')
+ req = webob.Request.blank('/v1.0/testacct/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
def test_delete_server_instance(self):
- req = webob.Request.blank('/v1.0/servers/1')
+ req = webob.Request.blank('/v1.0/testacct/servers/1')
req.method = 'DELETE'
self.server_delete_called = False
diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py
new file mode 100644
index 000000000..bd32254cd
--- /dev/null
+++ b/nova/tests/api/openstack/test_users.py
@@ -0,0 +1,139 @@
+# Copyright 2010 OpenStack LLC.
+# 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 stubout
+import webob
+import json
+
+import nova.api
+import nova.api.openstack.auth
+from nova import context
+from nova import flags
+from nova import test
+from nova.auth.manager import User, Project
+from nova.tests.api.openstack import fakes
+
+
+FLAGS = flags.FLAGS
+FLAGS.verbose = True
+
+
+def fake_init(self):
+ self.manager = fakes.FakeAuthManager()
+
+
+def fake_admin_check(self, req):
+ return True
+
+
+class UsersTest(test.TestCase):
+ def setUp(self):
+ super(UsersTest, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(nova.api.openstack.users.Controller, '__init__',
+ fake_init)
+ self.stubs.Set(nova.api.openstack.users.Controller, '_check_admin',
+ fake_admin_check)
+ fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.projects = dict(testacct=Project('testacct',
+ 'testacct',
+ 'guy1',
+ 'test',
+ []))
+ fakes.FakeAuthDatabase.data = {}
+ fakes.stub_out_networking(self.stubs)
+ fakes.stub_out_rate_limiting(self.stubs)
+ fakes.stub_out_auth(self.stubs)
+
+ self.allow_admin = FLAGS.allow_admin_api
+ FLAGS.allow_admin_api = True
+ fakemgr = fakes.FakeAuthManager()
+ fakemgr.add_user('acc1', User('guy1', 'guy1', 'acc1',
+ 'fortytwo!', False))
+ fakemgr.add_user('acc2', User('guy2', 'guy2', 'acc2',
+ 'swordfish', True))
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ FLAGS.allow_admin_api = self.allow_admin
+ super(UsersTest, self).tearDown()
+
+ def test_get_user_list(self):
+ req = webob.Request.blank('/v1.0/testacct/users')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(len(res_dict['users']), 2)
+
+ def test_get_user_by_id(self):
+ req = webob.Request.blank('/v1.0/testacct/users/guy2')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res_dict['user']['id'], 'guy2')
+ self.assertEqual(res_dict['user']['name'], 'guy2')
+ self.assertEqual(res_dict['user']['secret'], 'swordfish')
+ self.assertEqual(res_dict['user']['admin'], True)
+ self.assertEqual(res.status_int, 200)
+
+ def test_user_delete(self):
+ req = webob.Request.blank('/v1.0/testacct/users/guy1')
+ req.method = 'DELETE'
+ res = req.get_response(fakes.wsgi_app())
+ self.assertTrue('guy1' not in [u.id for u in
+ fakes.FakeAuthManager.auth_data.values()])
+ self.assertEqual(res.status_int, 200)
+
+ def test_user_create(self):
+ body = dict(user=dict(name='test_guy',
+ access='acc3',
+ secret='invasionIsInNormandy',
+ admin=True))
+ req = webob.Request.blank('/v1.0/testacct/users')
+ req.method = 'POST'
+ req.body = json.dumps(body)
+
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res_dict['user']['id'], 'test_guy')
+ self.assertEqual(res_dict['user']['name'], 'test_guy')
+ self.assertEqual(res_dict['user']['access'], 'acc3')
+ self.assertEqual(res_dict['user']['secret'], 'invasionIsInNormandy')
+ self.assertEqual(res_dict['user']['admin'], True)
+ self.assertTrue('test_guy' in [u.id for u in
+ fakes.FakeAuthManager.auth_data.values()])
+ self.assertEqual(len(fakes.FakeAuthManager.auth_data.values()), 3)
+
+ def test_user_update(self):
+ body = dict(user=dict(name='guy2',
+ access='acc2',
+ secret='invasionIsInNormandy'))
+ req = webob.Request.blank('/v1.0/testacct/users/guy2')
+ req.method = 'PUT'
+ req.body = json.dumps(body)
+
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res_dict['user']['id'], 'guy2')
+ self.assertEqual(res_dict['user']['name'], 'guy2')
+ self.assertEqual(res_dict['user']['access'], 'acc2')
+ self.assertEqual(res_dict['user']['secret'], 'invasionIsInNormandy')
+ self.assertEqual(res_dict['user']['admin'], True)
diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py
index 555b206b9..51f13af48 100644
--- a/nova/tests/api/openstack/test_zones.py
+++ b/nova/tests/api/openstack/test_zones.py
@@ -64,7 +64,7 @@ class ZonesTest(test.TestCase):
def setUp(self):
super(ZonesTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
- fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -85,7 +85,7 @@ class ZonesTest(test.TestCase):
super(ZonesTest, self).tearDown()
def test_get_zone_list(self):
- req = webob.Request.blank('/v1.0/zones')
+ req = webob.Request.blank('/v1.0/testacct/zones')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -93,7 +93,7 @@ class ZonesTest(test.TestCase):
self.assertEqual(len(res_dict['zones']), 2)
def test_get_zone_by_id(self):
- req = webob.Request.blank('/v1.0/zones/1')
+ req = webob.Request.blank('/v1.0/testacct/zones/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -103,7 +103,7 @@ class ZonesTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_zone_delete(self):
- req = webob.Request.blank('/v1.0/zones/1')
+ req = webob.Request.blank('/v1.0/testacct/zones/1')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
@@ -111,7 +111,7 @@ class ZonesTest(test.TestCase):
def test_zone_create(self):
body = dict(zone=dict(api_url='http://blah.zoo', username='fred',
password='fubar'))
- req = webob.Request.blank('/v1.0/zones')
+ req = webob.Request.blank('/v1.0/testacct/zones')
req.method = 'POST'
req.body = json.dumps(body)
@@ -125,7 +125,7 @@ class ZonesTest(test.TestCase):
def test_zone_update(self):
body = dict(zone=dict(username='zeb', password='sneaky'))
- req = webob.Request.blank('/v1.0/zones/1')
+ req = webob.Request.blank('/v1.0/testacct/zones/1')
req.method = 'PUT'
req.body = json.dumps(body)
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index 7531af4ec..a45d32cb2 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -207,7 +207,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port):
'transfer-encoding': 'chunked',
'x-image-meta-is_public': 'True',
'x-image-meta-status': 'queued',
- 'x-image-meta-type': 'vhd'
+ 'x-image-meta-type': 'vhd',
}
for header, value in headers.iteritems():
conn.putheader(header, value)
--
cgit
From 137a4946785b9460aadb9fe40f2b0e18bd7f6063 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Fri, 4 Mar 2011 01:09:21 +0900
Subject: Merged to trunk rev 757. Main changes are below. 1. Rename db table
ComputeService -> ComputeNode 2. nova-manage option instance_type is reserved
and we cannot use option instance, so change instance -> vm.
---
bin/nova-manage | 4 ++--
nova/db/api.py | 12 +++++-----
nova/db/sqlalchemy/api.py | 26 +++++++++++-----------
.../versions/009_add_live_migration.py | 8 +++----
nova/db/sqlalchemy/models.py | 10 ++++-----
nova/scheduler/driver.py | 10 ++++-----
nova/scheduler/manager.py | 14 ++++++------
nova/tests/test_scheduler.py | 16 ++++++-------
nova/tests/test_virt.py | 22 +++++++++---------
nova/virt/libvirt_conn.py | 10 ++++-----
10 files changed, 66 insertions(+), 66 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index f41950cd2..d782f6028 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -546,7 +546,7 @@ class NetworkCommands(object):
network.dns)
-class InstanceCommands(object):
+class VmCommands(object):
"""Class for mangaging VM instances."""
def live_migration(self, ec2_id, dest):
@@ -831,7 +831,7 @@ CATEGORIES = [
('fixed', FixedIpCommands),
('floating', FloatingIpCommands),
('network', NetworkCommands),
- ('instance', InstanceCommands),
+ ('vm', VmCommands),
('service', ServiceCommands),
('log', LogCommands),
('db', DbCommands),
diff --git a/nova/db/api.py b/nova/db/api.py
index 13bc07ad2..3b427cefe 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -153,24 +153,24 @@ def service_update(context, service_id, values):
###################
-def compute_service_get(context, compute_id, session=None):
+def compute_node_get(context, compute_id, session=None):
"""Get an computeService or raise if it does not exist."""
- return IMPL.compute_service_get(context, compute_id)
+ return IMPL.compute_node_get(context, compute_id)
-def compute_service_create(context, values):
+def compute_node_create(context, values):
"""Create a computeService from the values dictionary."""
- return IMPL.compute_service_create(context, values)
+ return IMPL.compute_node_create(context, values)
-def compute_service_update(context, compute_id, values):
+def compute_node_update(context, compute_id, values):
"""Set the given properties on an computeService and update it.
Raises NotFound if computeService does not exist.
"""
- return IMPL.compute_service_update(context, compute_id, values)
+ return IMPL.compute_node_update(context, compute_id, values)
###################
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index bed621b18..69aa07279 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -119,8 +119,8 @@ def service_destroy(context, service_id):
service_ref.delete(session=session)
if service_ref.topic == 'compute' and \
- len(service_ref.compute_service) != 0:
- for c in service_ref.compute_service:
+ len(service_ref.compute_node) != 0:
+ for c in service_ref.compute_node:
c.delete(session=session)
@@ -130,7 +130,7 @@ def service_get(context, service_id, session=None):
session = get_session()
result = session.query(models.Service).\
- options(joinedload('compute_service')).\
+ options(joinedload('compute_node')).\
filter_by(id=service_id).\
filter_by(deleted=can_read_deleted(context)).\
first()
@@ -174,7 +174,7 @@ def service_get_all_compute_by_host(context, host):
topic = 'compute'
session = get_session()
result = session.query(models.Service).\
- options(joinedload('compute_service')).\
+ options(joinedload('compute_node')).\
filter_by(deleted=False).\
filter_by(host=host).\
filter_by(topic=topic).\
@@ -298,11 +298,11 @@ def service_update(context, service_id, values):
@require_admin_context
-def compute_service_get(context, compute_id, session=None):
+def compute_node_get(context, compute_id, session=None):
if not session:
session = get_session()
- result = session.query(models.ComputeService).\
+ result = session.query(models.ComputeNode).\
filter_by(id=compute_id).\
filter_by(deleted=can_read_deleted(context)).\
first()
@@ -314,18 +314,18 @@ def compute_service_get(context, compute_id, session=None):
@require_admin_context
-def compute_service_create(context, values):
- compute_service_ref = models.ComputeService()
- compute_service_ref.update(values)
- compute_service_ref.save()
- return compute_service_ref
+def compute_node_create(context, values):
+ compute_node_ref = models.ComputeNode()
+ compute_node_ref.update(values)
+ compute_node_ref.save()
+ return compute_node_ref
@require_admin_context
-def compute_service_update(context, compute_id, values):
+def compute_node_update(context, compute_id, values):
session = get_session()
with session.begin():
- compute_ref = compute_service_get(context, compute_id, session=session)
+ compute_ref = compute_node_get(context, compute_id, session=session)
compute_ref.update(values)
compute_ref.save(session=session)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/009_add_live_migration.py b/nova/db/sqlalchemy/migrate_repo/versions/009_add_live_migration.py
index 2689b5b74..23ccccb4e 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/009_add_live_migration.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/009_add_live_migration.py
@@ -31,7 +31,7 @@ instances = Table('instances', meta,
# New Tables
#
-compute_services = Table('compute_services', meta,
+compute_nodes = Table('compute_nodes', meta,
Column('created_at', DateTime(timezone=False)),
Column('updated_at', DateTime(timezone=False)),
Column('deleted_at', DateTime(timezone=False)),
@@ -73,11 +73,11 @@ def upgrade(migrate_engine):
meta.bind = migrate_engine
try:
- compute_services.create()
+ compute_nodes.create()
except Exception:
- logging.info(repr(compute_services))
+ logging.info(repr(compute_nodes))
logging.exception('Exception while creating table')
- meta.drop_all(tables=[compute_services])
+ meta.drop_all(tables=[compute_nodes])
raise
instances.create_column(instances_launched_on)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 2af7377ef..8646190f3 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -113,18 +113,18 @@ class Service(BASE, NovaBase):
availability_zone = Column(String(255), default='nova')
-class ComputeService(BASE, NovaBase):
+class ComputeNode(BASE, NovaBase):
"""Represents a running compute service on a host."""
- __tablename__ = 'compute_services'
+ __tablename__ = 'compute_nodes'
id = Column(Integer, primary_key=True)
service_id = Column(Integer, ForeignKey('services.id'), nullable=True)
service = relationship(Service,
- backref=backref('compute_service'),
+ backref=backref('compute_node'),
foreign_keys=service_id,
primaryjoin='and_('
- 'ComputeService.service_id == Service.id,'
- 'ComputeService.deleted == False)')
+ 'ComputeNode.service_id == Service.id,'
+ 'ComputeNode.deleted == False)')
vcpus = Column(Integer, nullable=True)
memory_mb = Column(Integer, nullable=True)
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 4485ba39f..791f9000d 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -190,7 +190,7 @@ class Scheduler(object):
# Checking dest exists.
dservice_refs = db.service_get_all_compute_by_host(context, dest)
- dservice_ref = dservice_refs[0]['compute_service'][0]
+ dservice_ref = dservice_refs[0]['compute_node'][0]
# Checking original host( where instance was launched at) exists.
try:
@@ -200,7 +200,7 @@ class Scheduler(object):
raise exception.Invalid(_("host %s where instance was launched "
"does not exist.")
% instance_ref['launched_on'])
- oservice_ref = oservice_refs[0]['compute_service'][0]
+ oservice_ref = oservice_refs[0]['compute_node'][0]
# Checking hypervisor is same.
orig_hypervisor = oservice_ref['hypervisor_type']
@@ -252,10 +252,10 @@ class Scheduler(object):
# Getting host information
service_refs = db.service_get_all_compute_by_host(context, dest)
- compute_service_ref = service_refs[0]['compute_service'][0]
+ compute_node_ref = service_refs[0]['compute_node'][0]
- mem_total = int(compute_service_ref['memory_mb'])
- mem_used = int(compute_service_ref['memory_mb_used'])
+ mem_total = int(compute_node_ref['memory_mb'])
+ mem_used = int(compute_node_ref['memory_mb_used'])
mem_avail = mem_total - mem_used
mem_inst = instance_ref['memory_mb']
if mem_avail <= mem_inst:
diff --git a/nova/scheduler/manager.py b/nova/scheduler/manager.py
index a50d3ab20..090d8b89d 100644
--- a/nova/scheduler/manager.py
+++ b/nova/scheduler/manager.py
@@ -87,13 +87,13 @@ class SchedulerManager(manager.Manager):
compute_ref = compute_ref[0]
# Getting physical resource information
- compute_service_ref = compute_ref['compute_service'][0]
- resource = {'vcpus': compute_service_ref['vcpus'],
- 'memory_mb': compute_service_ref['memory_mb'],
- 'local_gb': compute_service_ref['local_gb'],
- 'vcpus_used': compute_service_ref['vcpus_used'],
- 'memory_mb_used': compute_service_ref['memory_mb_used'],
- 'local_gb_used': compute_service_ref['local_gb_used']}
+ compute_node_ref = compute_ref['compute_node'][0]
+ resource = {'vcpus': compute_node_ref['vcpus'],
+ 'memory_mb': compute_node_ref['memory_mb'],
+ 'local_gb': compute_node_ref['local_gb'],
+ 'vcpus_used': compute_node_ref['vcpus_used'],
+ 'memory_mb_used': compute_node_ref['memory_mb_used'],
+ 'local_gb_used': compute_node_ref['local_gb_used']}
# Getting usage resource information
usage = {}
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index 62db42b11..711b66af7 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -60,7 +60,7 @@ class SchedulerTestCase(test.TestCase):
self.flags(scheduler_driver='nova.tests.test_scheduler.TestDriver')
def _create_compute_service(self):
- """Create compute-manager(ComputeService and Service record)."""
+ """Create compute-manager(ComputeNode and Service record)."""
ctxt = context.get_admin_context()
dic = {'host': 'dummy', 'binary': 'nova-compute', 'topic': 'compute',
'report_count': 0, 'availability_zone': 'dummyzone'}
@@ -71,7 +71,7 @@ class SchedulerTestCase(test.TestCase):
'vcpus_used': 16, 'memory_mb_used': 32, 'local_gb_used': 10,
'hypervisor_type': 'qemu', 'hypervisor_version': 12003,
'cpu_info': ''}
- db.compute_service_create(ctxt, dic)
+ db.compute_node_create(ctxt, dic)
return db.service_get(ctxt, s_ref['id'])
@@ -144,8 +144,8 @@ class SchedulerTestCase(test.TestCase):
# result checking
c1 = ('resource' in result and 'usage' in result)
- compute_service = s_ref['compute_service'][0]
- c2 = self._dic_is_equal(result['resource'], compute_service)
+ compute_node = s_ref['compute_node'][0]
+ c2 = self._dic_is_equal(result['resource'], compute_node)
c3 = result['usage'] == {}
self.assertTrue(c1 and c2 and c3)
db.service_destroy(ctxt, s_ref['id'])
@@ -163,8 +163,8 @@ class SchedulerTestCase(test.TestCase):
result = scheduler.show_host_resources(ctxt, s_ref['host'])
c1 = ('resource' in result and 'usage' in result)
- compute_service = s_ref['compute_service'][0]
- c2 = self._dic_is_equal(result['resource'], compute_service)
+ compute_node = s_ref['compute_node'][0]
+ c2 = self._dic_is_equal(result['resource'], compute_node)
c3 = result['usage'].keys() == ['p-01', 'p-02']
keys = ['vcpus', 'memory_mb', 'local_gb']
c4 = self._dic_is_equal(result['usage']['p-01'], i_ref1, keys)
@@ -301,7 +301,7 @@ class SimpleDriverTestCase(test.TestCase):
dic['memory_mb_used'] = kwargs.get('memory_mb_used', 32)
dic['hypervisor_type'] = kwargs.get('hypervisor_type', 'qemu')
dic['hypervisor_version'] = kwargs.get('hypervisor_version', 12003)
- db.compute_service_create(self.context, dic)
+ db.compute_node_create(self.context, dic)
return db.service_get(self.context, s_ref['id'])
def test_doesnt_report_disabled_hosts_as_up(self):
@@ -923,7 +923,7 @@ class SimpleDriverTestCase(test.TestCase):
self.mox.StubOutWithMock(rpc, 'call', use_mock_anything=True)
rpc.call(mox.IgnoreArg(), mox.IgnoreArg(),
{"method": 'compare_cpu',
- "args": {'cpu_info': s_ref2['compute_service'][0]['cpu_info']}}).\
+ "args": {'cpu_info': s_ref2['compute_node'][0]['cpu_info']}}).\
AndRaise(rpc.RemoteError("doesn't have compatibility to", "", ""))
self.mox.ReplayAll()
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index 17b80c294..aac55a894 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -275,7 +275,7 @@ class LibvirtConnTestCase(test.TestCase):
db.instance_destroy(user_context, instance_ref['id'])
def test_update_available_resource_works_correctly(self):
- """Confirm compute_service table is updated successfully."""
+ """Confirm compute_node table is updated successfully."""
org_path = FLAGS.instances_path = ''
FLAGS.instances_path = '.'
@@ -289,16 +289,16 @@ class LibvirtConnTestCase(test.TestCase):
conn = libvirt_conn.LibvirtConnection(False)
conn.update_available_resource(self.context, 'dummy')
service_ref = db.service_get(self.context, service_ref['id'])
- compute_service = service_ref['compute_service'][0]
-
- c1 = (compute_service['vcpus'] > 0)
- c2 = (compute_service['memory_mb'] > 0)
- c3 = (compute_service['local_gb'] > 0)
- c4 = (compute_service['vcpus_used'] == 0)
- c5 = (compute_service['memory_mb_used'] > 0)
- c6 = (compute_service['local_gb_used'] > 0)
- c7 = (len(compute_service['hypervisor_type']) > 0)
- c8 = (compute_service['hypervisor_version'] > 0)
+ compute_node = service_ref['compute_node'][0]
+
+ c1 = (compute_node['vcpus'] > 0)
+ c2 = (compute_node['memory_mb'] > 0)
+ c3 = (compute_node['local_gb'] > 0)
+ c4 = (compute_node['vcpus_used'] == 0)
+ c5 = (compute_node['memory_mb_used'] > 0)
+ c6 = (compute_node['local_gb_used'] > 0)
+ c7 = (len(compute_node['hypervisor_type']) > 0)
+ c8 = (compute_node['hypervisor_version'] > 0)
self.assertTrue(c1 and c2 and c3 and c4 and c5 and c6 and c7 and c8)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index b9abf1890..71ca508b0 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1021,7 +1021,7 @@ class LibvirtConnection(object):
self.firewall_driver.refresh_security_group_members(security_group_id)
def update_available_resource(self, ctxt, host):
- """Updates compute manager resource info on ComputeService table.
+ """Updates compute manager resource info on ComputeNode table.
This method is called when nova-coompute launches, and
whenever admin executes "nova-manage service update_resource".
@@ -1049,14 +1049,14 @@ class LibvirtConnection(object):
'hypervisor_version': self.get_hypervisor_version(),
'cpu_info': self.get_cpu_info()}
- compute_service_ref = service_ref['compute_service']
- if not compute_service_ref:
+ compute_node_ref = service_ref['compute_node']
+ if not compute_node_ref:
LOG.info(_('Compute_service record is created for %s ') % host)
dic['service_id'] = service_ref['id']
- db.compute_service_create(ctxt, dic)
+ db.compute_node_create(ctxt, dic)
else:
LOG.info(_('Compute_service record is updated for %s ') % host)
- db.compute_service_update(ctxt, compute_service_ref[0]['id'], dic)
+ db.compute_node_update(ctxt, compute_node_ref[0]['id'], dic)
def compare_cpu(self, cpu_info):
"""Checks the host cpu is compatible to a cpu given by xml.
--
cgit
From bc6cc457132b096150dcd9ff2ed2909585a80484 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Fri, 4 Mar 2011 01:17:05 +0900
Subject: some comments are modified
---
nova/db/api.py | 8 ++++----
nova/db/sqlalchemy/api.py | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/nova/db/api.py b/nova/db/api.py
index 3b427cefe..43e1c2183 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -154,19 +154,19 @@ def service_update(context, service_id, values):
def compute_node_get(context, compute_id, session=None):
- """Get an computeService or raise if it does not exist."""
+ """Get an computeNode or raise if it does not exist."""
return IMPL.compute_node_get(context, compute_id)
def compute_node_create(context, values):
- """Create a computeService from the values dictionary."""
+ """Create a computeNode from the values dictionary."""
return IMPL.compute_node_create(context, values)
def compute_node_update(context, compute_id, values):
- """Set the given properties on an computeService and update it.
+ """Set the given properties on an computeNode and update it.
- Raises NotFound if computeService does not exist.
+ Raises NotFound if computeNode does not exist.
"""
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 69aa07279..b305543ff 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -308,7 +308,7 @@ def compute_node_get(context, compute_id, session=None):
first()
if not result:
- raise exception.NotFound(_('No computeService for id %s') % compute_id)
+ raise exception.NotFound(_('No computeNode for id %s') % compute_id)
return result
--
cgit
From 9cfe8ff2e8e66952c3202b852a88ee6fca6fb736 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Thu, 3 Mar 2011 16:31:01 -0500
Subject: pep8
---
nova/api/openstack/servers.py | 9 +++++----
nova/tests/api/openstack/test_servers.py | 6 +++---
2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 73c787828..ea13116fa 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -85,6 +85,7 @@ def _translate_detail_keys(inst):
return dict(server=inst_dict)
+
def _translate_keys(inst):
""" Coerces into dictionary format, excluding all model attributes
save for id and name """
@@ -143,8 +144,8 @@ class Controller(wsgi.Controller):
"""
Create a list of onset files from the personality request attribute
- At this time, onset_files must be formatted as a list of
- (file_path, file_content) pairs for compatibility with the
+ At this time, onset_files must be formatted as a list of
+ (file_path, file_content) pairs for compatibility with the
underlying compute service.
"""
onset_files = []
@@ -157,8 +158,8 @@ class Controller(wsgi.Controller):
try:
contents = base64.b64decode(contents)
except TypeError:
- raise exc.HTTPBadRequest(explanation=
- 'Personality content for %s cannot be decoded' % path)
+ msg = 'Personality content for %s cannot be decoded' % path
+ raise exc.HTTPBadRequest(explanation=msg)
onset_files.append((path, contents))
return onset_files
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 272d34e3a..53cfa3a6e 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -287,8 +287,8 @@ class ServersTest(test.TestCase):
request, response, onset_files = \
self._create_instance_with_personality(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [(path, contents)])
-
+ self.assertEquals(onset_files, [(path, contents)])
+
def test_create_instance_with_personality_with_non_b64_content(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Oh no!"\n'
@@ -320,7 +320,7 @@ class ServersTest(test.TestCase):
request, response, onset_files = \
self._create_instance_with_personality(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [(path, contents)])
+ self.assertEquals(onset_files, [(path, contents)])
def test_create_instance_personality_not_a_list(self):
path = '/my/file/path'
--
cgit
From 417f6ca5c54878a6bea4d545126f93ecb6a043b4 Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Thu, 3 Mar 2011 22:22:00 +0000
Subject: localize a few error messages.
---
nova/api/openstack/accounts.py | 2 +-
nova/api/openstack/users.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/api/openstack/accounts.py b/nova/api/openstack/accounts.py
index 264fdab99..3b90d2776 100644
--- a/nova/api/openstack/accounts.py
+++ b/nova/api/openstack/accounts.py
@@ -47,7 +47,7 @@ class Controller(wsgi.Controller):
""" We cannot depend on the db layer to check for admin access
for the auth manager, so we do it here """
if not context.is_admin:
- raise exception.NotAuthorized("Not admin user.")
+ raise exception.NotAuthorized(_("Not admin user."))
def show(self, req, id):
"""Return data about the given account id"""
diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py
index c0b7544f9..ae3bf7791 100644
--- a/nova/api/openstack/users.py
+++ b/nova/api/openstack/users.py
@@ -48,7 +48,7 @@ class Controller(wsgi.Controller):
""" We cannot depend on the db layer to check for admin access
for the auth manager, so we do it here """
if not context.is_admin:
- raise exception.NotAuthorized("Not admin user")
+ raise exception.NotAuthorized(_("Not admin user"))
def index(self, req, **kw):
"""Return all users in brief"""
--
cgit
From e14f524eb92ae07704a2ec7dac0f97c60940a6ab Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Thu, 3 Mar 2011 17:49:41 -0500
Subject: enforce personality quotas
---
nova/compute/api.py | 24 ++++++++++++++++++
nova/quota.py | 27 ++++++++++++--------
nova/tests/test_quota.py | 66 +++++++++++++++++++++++++++++++++++++++++++-----
3 files changed, 101 insertions(+), 16 deletions(-)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 625778b66..44e583cd4 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -80,6 +80,26 @@ class API(base.Base):
topic,
{"method": "get_network_topic", "args": {'fake': 1}})
+ def _check_personality_file_quota(self, context, personality_files):
+ limit = quota.allowed_personality_files(context)
+ if len(personality_files) > limit:
+ raise quota.QuotaError(_("Personality limit exceeded. You can "
+ "only have %d personalities when "
+ "creating an instance.") % limit,
+ "PersonalityLimitExceeded")
+ path_limit = quota.allowed_personality_path_bytes(context)
+ content_limit = quota.allowed_personality_content_bytes(context)
+ for path, content in personality_files:
+ if len(path) > path_limit:
+ raise quota.QuotaError(
+ _("Personality file path limit exceeded."),
+ "PersonalityLimitExceeded")
+ if len(content) > content_limit:
+ raise quota.QuotaError(
+ _("Personality file content limit exceeded."),
+ "PersonalityLimitExceeded")
+ return personality_files
+
def create(self, context, instance_type,
image_id, kernel_id=None, ramdisk_id=None,
min_count=1, max_count=1,
@@ -124,6 +144,10 @@ class API(base.Base):
LOG.warn(msg)
raise quota.QuotaError(msg, "MetadataLimitExceeded")
+ if onset_files is not None:
+ onset_files = \
+ self._check_personality_file_quota(context, onset_files)
+
image = self.image_service.show(context, image_id)
if kernel_id is None:
kernel_id = image.get('kernel_id', None)
diff --git a/nova/quota.py b/nova/quota.py
index 14b388794..4b777624c 100644
--- a/nova/quota.py
+++ b/nova/quota.py
@@ -37,10 +37,12 @@ flags.DEFINE_integer('quota_floating_ips', 10,
'number of floating ips allowed per project')
flags.DEFINE_integer('quota_metadata_items', 128,
'number of metadata items allowed per instance')
-flags.DEFINE_integer('quota_file_injection_max_files', 5,
- 'number of files allowed during file injection')
-flags.DEFINE_integer('quota_file_injection_max_file_bytes', 10 * 1024,
- 'number of bytes allowed per file during file injection')
+flags.DEFINE_integer('quota_personality_max_files', 5,
+ 'number of personality files allowed')
+flags.DEFINE_integer('quota_personality_max_content_bytes', 10 * 1024,
+ 'number of bytes allowed per personality file')
+flags.DEFINE_integer('quota_personality_max_path_bytes', 255,
+ 'number of bytes allowed per personality file path')
def get_quota(context, project_id):
@@ -113,14 +115,19 @@ def allowed_metadata_items(context, num_metadata_items):
return min(num_metadata_items, num_allowed_metadata_items)
-def allowed_file_injection_files(context):
- """Return the number of files allowed per file injection"""
- return FLAGS.quota_file_injection_max_files
+def allowed_personality_files(context):
+ """Return the number of personality files allowed"""
+ return FLAGS.quota_personality_max_files
-def allowed_file_injection_file_bytes(context):
- """Return the number of bytes allowed per file during injection"""
- return FLAGS.quota_file_injection_max_file_bytes
+def allowed_personality_content_bytes(context):
+ """Return the number of bytes allowed per personality content"""
+ return FLAGS.quota_personality_max_content_bytes
+
+
+def allowed_personality_path_bytes(context):
+ """Return the number of bytes allowed in a personality file path"""
+ return FLAGS.quota_personality_max_path_bytes
class QuotaError(exception.ApiError):
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index 48e5a5538..16a083788 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -177,12 +177,66 @@ class QuotaTestCase(test.TestCase):
image_id='fake',
metadata=metadata)
- def test_allowed_file_injection_files(self):
+ def test_allowed_personality_files(self):
self.assertEqual(
- quota.allowed_file_injection_files(self.context),
- FLAGS.quota_file_injection_max_files)
+ quota.allowed_personality_files(self.context),
+ FLAGS.quota_personality_max_files)
+
+ def _create_with_personality(self, files):
+ api = compute.API()
+ api.create(self.context, min_count=1, max_count=1,
+ instance_type='m1.small', image_id='fake',
+ onset_files=files)
+
+ def test_no_personality_files(self):
+ api = compute.API()
+ api.create(self.context, instance_type='m1.small', image_id='fake')
+
+ def test_max_personality_files(self):
+ files = []
+ for i in xrange(FLAGS.quota_personality_max_files):
+ files.append(('/my/path%d' % i, 'config = test\n'))
+ self._create_with_personality(files) # no QuotaError
+
+ def test_too_many_personality_files(self):
+ files = []
+ for i in xrange(FLAGS.quota_personality_max_files + 1):
+ files.append(('/my/path%d' % i, 'my\ncontent%d\n' % i))
+ self.assertRaises(quota.QuotaError,
+ self._create_with_personality, files)
- def test_allowed_file_injection_file_bytes(self):
+ def test_allowed_personality_content_bytes(self):
self.assertEqual(
- quota.allowed_file_injection_file_bytes(self.context),
- FLAGS.quota_file_injection_max_file_bytes)
+ quota.allowed_personality_content_bytes(self.context),
+ FLAGS.quota_personality_max_content_bytes)
+
+ def test_max_personality_content_bytes(self):
+ max = FLAGS.quota_personality_max_content_bytes
+ content = ''.join(['a' for i in xrange(max)])
+ files = [('/test/path', content)]
+ self._create_with_personality(files) # no QuotaError
+
+ def test_too_many_personality_content_bytes(self):
+ max = FLAGS.quota_personality_max_content_bytes
+ content = ''.join(['a' for i in xrange(max + 1)])
+ files = [('/test/path', content)]
+ self.assertRaises(quota.QuotaError,
+ self._create_with_personality, files)
+
+ def test_allowed_personality_path_bytes(self):
+ self.assertEqual(
+ quota.allowed_personality_path_bytes(self.context),
+ FLAGS.quota_personality_max_path_bytes)
+
+ def test_max_personality_path_bytes(self):
+ max = FLAGS.quota_personality_max_path_bytes
+ path = ''.join(['a' for i in xrange(max)])
+ files = [(path, 'config = quotatest')]
+ self._create_with_personality(files) # no QuotaError
+
+ def test_too_many_personality_path_bytes(self):
+ max = FLAGS.quota_personality_max_path_bytes
+ path = ''.join(['a' for i in xrange(max + 1)])
+ files = [(path, 'config = quotatest')]
+ self.assertRaises(quota.QuotaError,
+ self._create_with_personality, files)
--
cgit
From c5bfab9a0d213cee549371f05e74747cfcd8f998 Mon Sep 17 00:00:00 2001
From: Ryan Lane
Date: Thu, 3 Mar 2011 23:05:00 +0000
Subject: Changing output of status from showing the user as the owner, to
showing the project
---
nova/api/ec2/cloud.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index b1917e9ea..cadda97db 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -562,7 +562,7 @@ class CloudController(object):
if context.is_admin:
v['status'] = '%s (%s, %s, %s, %s)' % (
volume['status'],
- volume['user_id'],
+ volume['project_id'],
volume['host'],
instance_data,
volume['mountpoint'])
--
cgit
From 5ae13551990be67e3509ddcd10d1872a91634d83 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Thu, 3 Mar 2011 18:27:57 -0500
Subject: rename onset_files to personality_files all the way down to compute
manager
---
nova/api/openstack/servers.py | 16 +++++++--------
nova/compute/api.py | 10 ++++-----
nova/compute/manager.py | 2 +-
nova/tests/api/openstack/test_servers.py | 35 ++++++++++++++++----------------
nova/tests/test_quota.py | 2 +-
5 files changed, 33 insertions(+), 32 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index ea13116fa..8f6d8de66 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -140,15 +140,15 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
- def _get_onset_files_from_personality(self, personality):
+ def _get_personality_files(self, personality):
"""
- Create a list of onset files from the personality request attribute
+ Create a list of personality files from the personality attribute
- At this time, onset_files must be formatted as a list of
+ At this time, personality_files must be formatted as a list of
(file_path, file_content) pairs for compatibility with the
underlying compute service.
"""
- onset_files = []
+ personality_files = []
for item in personality:
try:
path = item['path']
@@ -160,8 +160,8 @@ class Controller(wsgi.Controller):
except TypeError:
msg = 'Personality content for %s cannot be decoded' % path
raise exc.HTTPBadRequest(explanation=msg)
- onset_files.append((path, contents))
- return onset_files
+ personality_files.append((path, contents))
+ return personality_files
def create(self, req):
""" Creates a new server for a given user """
@@ -191,7 +191,7 @@ class Controller(wsgi.Controller):
metadata.append({'key': k, 'value': v})
personality = env['server'].get('personality', [])
- onset_files = self._get_onset_files_from_personality(personality)
+ personality_files = self._get_personality_files(personality)
instances = self.compute_api.create(
context,
@@ -204,7 +204,7 @@ class Controller(wsgi.Controller):
key_name=key_pair['name'],
key_data=key_pair['public_key'],
metadata=metadata,
- onset_files=onset_files)
+ personality_files=personality_files)
return _translate_keys(instances[0])
def update(self, req, id):
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 44e583cd4..13938dcde 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -106,7 +106,7 @@ class API(base.Base):
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata=[],
- onset_files=None):
+ personality_files=None):
"""Create the number of instances requested if quota and
other arguments check out ok.
"""
@@ -144,9 +144,9 @@ class API(base.Base):
LOG.warn(msg)
raise quota.QuotaError(msg, "MetadataLimitExceeded")
- if onset_files is not None:
- onset_files = \
- self._check_personality_file_quota(context, onset_files)
+ if personality_files is not None:
+ personality_files = \
+ self._check_personality_file_quota(context, personality_files)
image = self.image_service.show(context, image_id)
if kernel_id is None:
@@ -242,7 +242,7 @@ class API(base.Base):
"args": {"topic": FLAGS.compute_topic,
"instance_id": instance_id,
"availability_zone": availability_zone,
- "onset_files": onset_files}})
+ "personality_files": personality_files}})
for group_id in security_groups:
self.trigger_security_group_members_refresh(elevated, group_id)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index d659712ad..1a392dda8 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -174,7 +174,7 @@ class ComputeManager(manager.Manager):
"""Launch a new instance with specified options."""
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
- instance_ref.onset_files = kwargs.get('onset_files', [])
+ instance_ref.onset_files = kwargs.get('personality_files', [])
if instance_ref['name'] in self.driver.list_instances():
raise exception.Error(_("Instance has already been created"))
LOG.audit(_("instance %s: starting..."), instance_id,
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 53cfa3a6e..bf934113a 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -241,13 +241,13 @@ class ServersTest(test.TestCase):
class FakeComputeAPI(object):
def __init__(self):
- self.onset_files = None
+ self.personality_files = None
def create(self, *args, **kwargs):
- if 'onset_files' in kwargs:
- self.onset_files = kwargs['onset_files']
+ if 'personality_files' in kwargs:
+ self.personality_files = kwargs['personality_files']
else:
- self.onset_files = None
+ self.personality_files = None
return [{'id': '1234', 'display_name': 'fakeinstance'}]
def make_stub_method(canned_return):
@@ -271,32 +271,33 @@ class ServersTest(test.TestCase):
req = webob.Request.blank('/v1.0/servers')
req.method = 'POST'
req.body = json.dumps(body)
- return req, req.get_response(fakes.wsgi_app()), compute_api.onset_files
+ return (req, req.get_response(fakes.wsgi_app()),
+ compute_api.personality_files)
def test_create_instance_with_no_personality(self):
- request, response, onset_files = \
+ request, response, personality_files = \
self._create_instance_with_personality(personality=None)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [])
+ self.assertEquals(personality_files, [])
def test_create_instance_with_personality(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Hello, World!"\n'
b64contents = base64.b64encode(contents)
personality = [self._personality_dict(path, b64contents)]
- request, response, onset_files = \
+ request, response, personality_files = \
self._create_instance_with_personality(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [(path, contents)])
+ self.assertEquals(personality_files, [(path, contents)])
def test_create_instance_with_personality_with_non_b64_content(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Oh no!"\n'
personality = [self._personality_dict(path, contents)]
- request, response, onset_files = \
+ request, response, personality_files = \
self._create_instance_with_personality(personality)
self.assertEquals(response.status_int, 400)
- self.assertEquals(onset_files, None)
+ self.assertEquals(personality_files, None)
def test_create_instance_with_three_personalities(self):
files = [
@@ -308,28 +309,28 @@ class ServersTest(test.TestCase):
for path, content in files:
personality.append(self._personality_dict(
path, base64.b64encode(content)))
- request, response, onset_files = \
+ request, response, personality_files = \
self._create_instance_with_personality(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, files)
+ self.assertEquals(personality_files, files)
def test_create_instance_personality_empty_content(self):
path = '/my/file/path'
contents = ''
personality = [self._personality_dict(path, contents)]
- request, response, onset_files = \
+ request, response, personality_files = \
self._create_instance_with_personality(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [(path, contents)])
+ self.assertEquals(personality_files, [(path, contents)])
def test_create_instance_personality_not_a_list(self):
path = '/my/file/path'
contents = 'myfilecontents'
personality = self._personality_dict(path, contents)
- request, response, onset_files = \
+ request, response, personality_files = \
self._create_instance_with_personality(personality)
self.assertEquals(response.status_int, 400)
- self.assertEquals(onset_files, None)
+ self.assertEquals(personality_files, None)
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index 16a083788..b26dec61a 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -186,7 +186,7 @@ class QuotaTestCase(test.TestCase):
api = compute.API()
api.create(self.context, min_count=1, max_count=1,
instance_type='m1.small', image_id='fake',
- onset_files=files)
+ personality_files=files)
def test_no_personality_files(self):
api = compute.API()
--
cgit
From 0a9ba675c88ae0b2a18f47524d24075409261658 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Thu, 3 Mar 2011 15:39:23 -0800
Subject: altered ra_server name to gateway_v6
---
.../versions/007_add_ipv6_flatmanager.py | 14 ++--
.../versions/007_add_ipv6_to_fixed_ips.py | 90 ----------------------
2 files changed, 6 insertions(+), 98 deletions(-)
delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
index e09f46652..937712970 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
@@ -28,7 +28,10 @@ meta = MetaData()
#
networks = Table('networks', meta,
Column('id', Integer(), primary_key=True, nullable=False),
- )
+ Column('ra_server', String(length=255, convert_unicode=False,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False))
+ )
#
# New Tables
@@ -43,12 +46,6 @@ networks = Table('networks', meta,
#
# Columns to add to existing tables
#
-
-networks_gateway_v6 = Column(
- 'gateway_v6',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False))
-
networks_netmask_v6 = Column(
'netmask_v6',
String(length=255, convert_unicode=False, assert_unicode=None,
@@ -60,6 +57,7 @@ def upgrade(migrate_engine):
# bind migrate_engine to your metadata
meta.bind = migrate_engine
+ # Alter column name
+ networks.c.ra_server.alter(name='gateway_v6')
# Add columns to existing tables
- networks.create_column(networks_gateway_v6)
networks.create_column(networks_netmask_v6)
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
deleted file mode 100644
index 427934d53..000000000
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_to_fixed_ips.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright 2011 OpenStack LLC
-# 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.
-
-from sqlalchemy import *
-from migrate import *
-
-from nova import log as logging
-
-
-meta = MetaData()
-
-
-# Table stub-definitions
-# Just for the ForeignKey and column creation to succeed, these are not the
-# actual definitions of instances or services.
-#
-fixed_ips = Table(
- "fixed_ips",
- meta,
- Column(
- "id",
- Integer(),
- primary_key=True,
- nullable=False))
-
-#
-# New Tables
-#
-# None
-
-#
-# Tables to alter
-#
-# None
-
-#
-# Columns to add to existing tables
-#
-
-fixed_ips_addressV6 = Column(
- "addressV6",
- String(
- length=255,
- convert_unicode=False,
- assert_unicode=None,
- unicode_error=None,
- _warn_on_bytestring=False))
-
-
-fixed_ips_netmaskV6 = Column(
- "netmaskV6",
- String(
- length=3,
- convert_unicode=False,
- assert_unicode=None,
- unicode_error=None,
- _warn_on_bytestring=False))
-
-
-fixed_ips_gatewayV6 = Column(
- "gatewayV6",
- String(
- length=255,
- convert_unicode=False,
- assert_unicode=None,
- unicode_error=None,
- _warn_on_bytestring=False))
-
-
-def upgrade(migrate_engine):
- # Upgrade operations go here. Don't create your own engine;
- # bind migrate_engine to your metadata
- meta.bind = migrate_engine
-
- # Add columns to existing tables
- fixed_ips.create_column(fixed_ips_addressV6)
- fixed_ips.create_column(fixed_ips_netmaskV6)
- fixed_ips.create_column(fixed_ips_gatewayV6)
--
cgit
From 35be7d39866f6ac1017dd94d33d9c01f47a6bc74 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Thu, 3 Mar 2011 15:44:01 -0800
Subject: Removed properties added to fixed_ips by xs-ipv6 BP
---
nova/db/sqlalchemy/models.py | 3 ---
nova/network/manager.py | 2 +-
2 files changed, 1 insertion(+), 4 deletions(-)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 4c94cd3db..7b4683427 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -458,9 +458,6 @@ class FixedIp(BASE, NovaBase):
allocated = Column(Boolean, default=False)
leased = Column(Boolean, default=False)
reserved = Column(Boolean, default=False)
- addressV6 = Column(String(255))
- netmaskV6 = Column(String(3))
- gatewayV6 = Column(String(255))
class User(BASE, NovaBase):
diff --git a/nova/network/manager.py b/nova/network/manager.py
index fb6e16772..686ab9732 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -167,7 +167,7 @@ class NetworkManager(manager.Manager):
# with a network, or a cluster of computes with a network
# and use that network here with a method like
# network_get_by_compute_host
- network_ref = self.db.network_get_by_bridge(context,
+ network_ref = self.db.network_get_by_bridge(context.elevated(),
FLAGS.flat_network_bridge)
address = self.db.fixed_ip_associate_pool(context.elevated(),
network_ref['id'],
--
cgit
From aa09f87060c1d1885b7a557ff26a3c421ad42df8 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Thu, 3 Mar 2011 17:31:37 -0800
Subject: remove ra_server from model and fix migration issue while running
unit tests
---
.../versions/007_add_ipv6_flatmanager.py | 60 +++++++++++++++++++---
nova/db/sqlalchemy/models.py | 2 -
2 files changed, 54 insertions(+), 8 deletions(-)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
index 937712970..d14f52af1 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
@@ -12,6 +12,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
+from lib2to3.fixer_util import String
from sqlalchemy import *
from migrate import *
@@ -27,12 +28,59 @@ meta = MetaData()
# actual definitions of instances or services.
#
networks = Table('networks', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
Column('id', Integer(), primary_key=True, nullable=False),
- Column('ra_server', String(length=255, convert_unicode=False,
- assert_unicode=None, unicode_error=None,
- _warn_on_bytestring=False))
- )
-
+ Column('injected', Boolean(create_constraint=True, name=None)),
+ Column('cidr',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('netmask',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('bridge',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('gateway',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('broadcast',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('dns',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('vlan', Integer()),
+ Column('vpn_public_address',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('vpn_public_port', Integer()),
+ Column('vpn_private_address',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('dhcp_start',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('project_id',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('host',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('cidr_v6',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column(
+ 'ra_server',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column(
+ 'label',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False))
+ )
#
# New Tables
#
@@ -59,5 +107,5 @@ def upgrade(migrate_engine):
# Alter column name
networks.c.ra_server.alter(name='gateway_v6')
- # Add columns to existing tables
+ # Add new column to existing table
networks.create_column(networks_netmask_v6)
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 7b4683427..14ff46647 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -402,8 +402,6 @@ class Network(BASE, NovaBase):
cidr = Column(String(255), unique=True)
cidr_v6 = Column(String(255), unique=True)
- ra_server = Column(String(255))
-
gateway_v6 = Column(String(255))
netmask_v6 = Column(String(255))
netmask = Column(String(255))
--
cgit
From abd5779068f3b979fc79dec7a68549999c58092d Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Fri, 4 Mar 2011 01:36:29 -0500
Subject: remove ensure_b64_encoding
---
nova/compute/manager.py | 10 ++--------
nova/utils.py | 12 ------------
nova/virt/xenapi/vmops.py | 10 +++++-----
3 files changed, 7 insertions(+), 25 deletions(-)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index d915dc069..3a712fd97 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -34,7 +34,6 @@ terminating it.
:func:`nova.utils.import_object`
"""
-import base64
import datetime
import random
import string
@@ -353,15 +352,10 @@ class ComputeManager(manager.Manager):
LOG.warn(_('trying to inject a file into a non-running '
'instance: %(instance_id)s (state: %(instance_state)s '
'expected: %(expected_state)s)') % locals())
- # Files/paths *should* be base64-encoded at this point, but
- # double-check to make sure.
- b64_path = utils.ensure_b64_encoding(path)
- b64_contents = utils.ensure_b64_encoding(file_contents)
- plain_path = base64.b64decode(b64_path)
nm = instance_ref['name']
- msg = _('instance %(nm)s: injecting file to %(plain_path)s') % locals()
+ msg = _('instance %(nm)s: injecting file to %(path)s') % locals()
LOG.audit(msg)
- self.driver.inject_file(instance_ref, b64_path, b64_contents)
+ self.driver.inject_file(instance_ref, path, file_contents)
@exception.wrap_exception
@checks_instance_lock
diff --git a/nova/utils.py b/nova/utils.py
index 0cf91e0cc..02b71900c 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -491,18 +491,6 @@ def loads(s):
return json.loads(s)
-def ensure_b64_encoding(val):
- """Safety method to ensure that values expected to be base64-encoded
- actually are. If they are, the value is returned unchanged. Otherwise,
- the encoded value is returned.
- """
- try:
- dummy = base64.decode(val)
- return val
- except TypeError:
- return base64.b64encode(val)
-
-
def get_from_path(items, path):
""" Returns a list of items matching the specified path. Takes an
XPath-like expression e.g. prop1/prop2/prop3, and for each item in items,
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 9ac83efb0..89d58a664 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -19,6 +19,7 @@
Management class for VM-related functions (spawn, reboot, etc).
"""
+import base64
import json
import M2Crypto
import os
@@ -313,17 +314,16 @@ class VMOps(object):
task = self._session.call_xenapi("Async.VM.start", vm, False, False)
self._session.wait_for_task(task, instance.id)
- def inject_file(self, instance, b64_path, b64_contents):
+ def inject_file(self, instance, path, contents):
"""Write a file to the VM instance. The path to which it is to be
written and the contents of the file need to be supplied; both should
be base64-encoded to prevent errors with non-ASCII characters being
transmitted. If the agent does not support file injection, or the user
has disabled it, a NotImplementedError will be raised.
"""
- # Files/paths *should* be base64-encoded at this point, but
- # double-check to make sure.
- b64_path = utils.ensure_b64_encoding(b64_path)
- b64_contents = utils.ensure_b64_encoding(b64_contents)
+ # Files/paths must be base64-encoded for transmission to agent
+ b64_path = base64.b64encode(path)
+ b64_contents = base64.b64encode(contents)
# Need to uniquely identify this request.
transaction_id = str(uuid.uuid4())
--
cgit
From 1f0df07baac52379b122a9928200305dd9d2151f Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Sat, 5 Mar 2011 00:57:08 +0900
Subject: Fixed based on reviewer's comment. Main changes are below. 1.
get_vcpu_total()/get_memory_mb()/get_memory_mb_used() is changed for users
who used non-linux environment. 2. test code added to test_virt.
---
contrib/nova.sh | 1 +
nova/tests/test_virt.py | 163 ++++++++++++++++++++++++++++++++++++++--------
nova/virt/libvirt_conn.py | 12 +++-
3 files changed, 147 insertions(+), 29 deletions(-)
diff --git a/contrib/nova.sh b/contrib/nova.sh
index 1187f2728..cf5b3de11 100755
--- a/contrib/nova.sh
+++ b/contrib/nova.sh
@@ -76,6 +76,7 @@ if [ "$CMD" == "install" ]; then
sudo apt-get install -y python-migrate python-eventlet python-gflags python-ipy python-tempita
sudo apt-get install -y python-libvirt python-libxml2 python-routes python-cheetah
sudo apt-get install -y python-netaddr python-paste python-pastedeploy python-glance
+ sudo apt-get install -y python-multiprocessing
if [ "$USE_IPV6" == 1 ]; then
sudo apt-get install -y radvd
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index aac55a894..5bb31659b 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -15,6 +15,7 @@
# under the License.
import mox
+import sys
from xml.etree.ElementTree import fromstring as xml_to_tree
from xml.dom.minidom import parseString as xml_to_dom
@@ -27,11 +28,15 @@ from nova import test
from nova import utils
from nova.api.ec2 import cloud
from nova.auth import manager
+from nova.compute import manager as compute_manager
+from nova.compute import power_state
from nova.db.sqlalchemy import models
from nova.virt import libvirt_conn
+libvirt = None
FLAGS = flags.FLAGS
flags.DECLARE('instances_path', 'nova.compute.manager')
+flags.DECLARE('compute_driver', 'nova.compute.manager')
class LibvirtConnTestCase(test.TestCase):
@@ -73,31 +78,36 @@ class LibvirtConnTestCase(test.TestCase):
'bridge': 'br101',
'instance_type': 'm1.small'}
+ def lazy_load_library_exists(self):
+ """check if libvirt is available."""
+ # try to connect libvirt. if fail, skip test.
+ try:
+ import libvirt
+ import libxml2
+ except ImportError:
+ return False
+ global libvirt
+ libvirt = __import__('libvirt')
+ libvirt_conn.libvirt = __import__('libvirt')
+ libvirt_conn.libxml2 = __import__('libxml2')
+ return True
+
def create_fake_libvirt_mock(self, **kwargs):
"""Defining mocks for LibvirtConnection(libvirt is not used)."""
- # A fake libvirt.virtConnect
+ # A fake libvirt.virConnect
class FakeLibvirtConnection(object):
- def getVersion(self):
- return 12003
-
- def getType(self):
- return 'qemu'
-
- def getCapabilities(self):
- return 'qemu'
-
- def listDomainsID(self):
- return []
-
- def getCapabilitied(self):
- return
+ pass
# A fake libvirt_conn.IptablesFirewallDriver
class FakeIptablesFirewallDriver(object):
+
def __init__(self, **kwargs):
pass
+ def setattr(self, key, val):
+ self.__setattr__(key, val)
+
# Creating mocks
fake = FakeLibvirtConnection()
fakeip = FakeIptablesFirewallDriver
@@ -274,33 +284,54 @@ class LibvirtConnTestCase(test.TestCase):
self.assertEquals(uri, testuri)
db.instance_destroy(user_context, instance_ref['id'])
- def test_update_available_resource_works_correctly(self):
+ def tes1t_update_available_resource_works_correctly(self):
"""Confirm compute_node table is updated successfully."""
org_path = FLAGS.instances_path = ''
FLAGS.instances_path = '.'
+ # Prepare mocks
+ def getVersion():
+ return 12003
+
+ def getType():
+ return 'qemu'
+
+ def listDomainsID():
+ return []
+
service_ref = self.create_service(host='dummy')
- self.create_fake_libvirt_mock()
+ self.create_fake_libvirt_mock(getVersion=getVersion,
+ getType=getType,
+ listDomainsID=listDomainsID)
self.mox.StubOutWithMock(libvirt_conn.LibvirtConnection,
'get_cpu_info')
libvirt_conn.LibvirtConnection.get_cpu_info().AndReturn('cpuinfo')
+ # Start test
self.mox.ReplayAll()
conn = libvirt_conn.LibvirtConnection(False)
conn.update_available_resource(self.context, 'dummy')
service_ref = db.service_get(self.context, service_ref['id'])
compute_node = service_ref['compute_node'][0]
- c1 = (compute_node['vcpus'] > 0)
- c2 = (compute_node['memory_mb'] > 0)
- c3 = (compute_node['local_gb'] > 0)
- c4 = (compute_node['vcpus_used'] == 0)
- c5 = (compute_node['memory_mb_used'] > 0)
- c6 = (compute_node['local_gb_used'] > 0)
- c7 = (len(compute_node['hypervisor_type']) > 0)
- c8 = (compute_node['hypervisor_version'] > 0)
-
- self.assertTrue(c1 and c2 and c3 and c4 and c5 and c6 and c7 and c8)
+ if sys.platform.upper() == 'LINUX2':
+ self.assertTrue(compute_node['vcpus'] > 0)
+ self.assertTrue(compute_node['memory_mb'] > 0)
+ self.assertTrue(compute_node['local_gb'] > 0)
+ self.assertTrue(compute_node['vcpus_used'] == 0)
+ self.assertTrue(compute_node['memory_mb_used'] > 0)
+ self.assertTrue(compute_node['local_gb_used'] > 0)
+ self.assertTrue(len(compute_node['hypervisor_type']) > 0)
+ self.assertTrue(compute_node['hypervisor_version'] > 0)
+ else:
+ self.assertTrue(compute_node['vcpus'] > 0)
+ self.assertTrue(compute_node['memory_mb'] == 0)
+ self.assertTrue(compute_node['local_gb'] > 0)
+ self.assertTrue(compute_node['vcpus_used'] == 0)
+ self.assertTrue(compute_node['memory_mb_used'] == 0)
+ self.assertTrue(compute_node['local_gb_used'] > 0)
+ self.assertTrue(len(compute_node['hypervisor_type']) > 0)
+ self.assertTrue(compute_node['hypervisor_version'] > 0)
db.service_destroy(self.context, service_ref['id'])
FLAGS.instances_path = org_path
@@ -319,6 +350,84 @@ class LibvirtConnTestCase(test.TestCase):
FLAGS.instances_path = org_path
+ def test_ensure_filtering_rules_for_instance_timeout(self):
+ """ensure_filtering_fules_for_instance() finishes with timeout."""
+ # Skip if non-libvirt environment
+ if not self.lazy_load_library_exists():
+ return
+
+ # Preparing mocks
+ def fake_none(self):
+ return
+
+ def fake_raise(self):
+ raise libvirt.libvirtError('ERR')
+
+ self.create_fake_libvirt_mock(nwfilterLookupByName=fake_raise)
+ instance_ref = db.instance_create(self.context, self.test_instance)
+
+ # Start test
+ self.mox.ReplayAll()
+ try:
+ conn = libvirt_conn.LibvirtConnection(False)
+ conn.firewall_driver.setattr('setup_basic_filtering', fake_none)
+ conn.firewall_driver.setattr('prepare_instance_filter', fake_none)
+ conn.ensure_filtering_rules_for_instance(instance_ref)
+ except exception.Error, e:
+ c1 = (0 <= e.message.find('Timeout migrating for'))
+ self.assertTrue(c1)
+
+ db.instance_destroy(self.context, instance_ref['id'])
+
+ def test_live_migration_raises_exception(self):
+ """Confirms recover method is called when exceptions are raised."""
+ # Skip if non-libvirt environment
+ if not self.lazy_load_library_exists():
+ return
+
+ # Preparing data
+ self.compute = utils.import_object(FLAGS.compute_manager)
+ instance_dict = {'host': 'fake', 'state': power_state.RUNNING,
+ 'state_description': 'running'}
+ instance_ref = db.instance_create(self.context, self.test_instance)
+ instance_ref = db.instance_update(self.context, instance_ref['id'],
+ instance_dict)
+ vol_dict = {'status': 'migrating', 'size': 1}
+ volume_ref = db.volume_create(self.context, vol_dict)
+ db.volume_attached(self.context, volume_ref['id'], instance_ref['id'],
+ '/dev/fake')
+
+ # Preparing mocks
+ vdmock = self.mox.CreateMock(libvirt.virDomain)
+ self.mox.StubOutWithMock(vdmock, "migrateToURI")
+ vdmock.migrateToURI(FLAGS.live_migration_uri % 'dest',
+ mox.IgnoreArg(),
+ None, FLAGS.live_migration_bandwidth).\
+ AndRaise(libvirt.libvirtError('ERR'))
+
+ def fake_lookup(instance_name):
+ if instance_name == instance_ref.name:
+ return vdmock
+
+ self.create_fake_libvirt_mock(lookupByName=fake_lookup)
+
+ # Start test
+ self.mox.ReplayAll()
+ conn = libvirt_conn.LibvirtConnection(False)
+ self.assertRaises(libvirt.libvirtError,
+ conn._live_migration,
+ self.context, instance_ref, 'dest', '',
+ self.compute.recover_live_migration)
+
+ instance_ref = db.instance_get(self.context, instance_ref['id'])
+ self.assertTrue(instance_ref['state_description'] == 'running')
+ self.assertTrue(instance_ref['state'] == power_state.RUNNING)
+ volume_ref = db.volume_get(self.context, volume_ref['id'])
+ self.assertTrue(volume_ref['status'] == 'in-use')
+
+ db.volume_destroy(self.context, volume_ref['id'])
+ db.instance_destroy(self.context, instance_ref['id'])
+
def tearDown(self):
self.manager.delete_project(self.project)
self.manager.delete_user(self.user)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 71ca508b0..627a12a1c 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -36,8 +36,10 @@ Supports KVM, QEMU, UML, and XEN.
"""
+import multiprocessing
import os
import shutil
+import sys
import random
import subprocess
import time
@@ -858,7 +860,7 @@ class LibvirtConnection(object):
"""
- return open('/proc/cpuinfo').read().count('processor')
+ return multiprocessing.cpu_count()
def get_memory_mb_total(self):
"""Get the total memory size(MB) of physical computer.
@@ -867,6 +869,9 @@ class LibvirtConnection(object):
"""
+ if sys.platform.upper() != 'LINUX2':
+ return 0
+
meminfo = open('/proc/meminfo').read().split()
idx = meminfo.index('MemTotal:')
# transforming kb to mb.
@@ -905,6 +910,9 @@ class LibvirtConnection(object):
"""
+ if sys.platform.upper() != 'LINUX2':
+ return 0
+
m = open('/proc/meminfo').read().split()
idx1 = m.index('MemFree:')
idx2 = m.index('Buffers:')
@@ -1126,7 +1134,7 @@ class LibvirtConnection(object):
# wait for completion
timeout_count = range(FLAGS.live_migration_retry_count)
- while not timeout_count:
+ while timeout_count:
try:
filter_name = 'nova-instance-%s' % instance_ref.name
self._conn.nwfilterLookupByName(filter_name)
--
cgit
From 23291a5e1a0134aff5fe030b52d4335a6f2a18d9 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Sat, 5 Mar 2011 01:07:12 +0900
Subject: delete unnecessary DECLARE
---
nova/tests/test_virt.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index 5bb31659b..7ea8c0fb5 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -36,7 +36,6 @@ from nova.virt import libvirt_conn
libvirt = None
FLAGS = flags.FLAGS
flags.DECLARE('instances_path', 'nova.compute.manager')
-flags.DECLARE('compute_driver', 'nova.compute.manager')
class LibvirtConnTestCase(test.TestCase):
--
cgit
From 68d894be2ec3b4eaa14dc5c90143f45f7db1e4b8 Mon Sep 17 00:00:00 2001
From: Cory Wright
Date: Fri, 4 Mar 2011 17:48:28 +0000
Subject: * Tests to verify correct vm-params for Windows and Linux instances
---
nova/compute/api.py | 5 +-
.../versions/007_add_os_type_to_instances.py | 4 +-
nova/tests/db/fakes.py | 1 +
nova/tests/test_xenapi.py | 99 ++++++++++++++++++----
nova/virt/xenapi/vm_utils.py | 14 +--
nova/virt/xenapi/vmops.py | 8 +-
6 files changed, 101 insertions(+), 30 deletions(-)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 8bdf712a0..d79371e94 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -125,7 +125,10 @@ class API(base.Base):
raise quota.QuotaError(msg, "MetadataLimitExceeded")
image = self.image_service.show(context, image_id)
- os_type = image['properties'].get('os_type', 'linux')
+
+ os_type = None
+ if 'properties' in image and 'os_type' in image['properties']:
+ os_type = image['properties']['os_type']
if kernel_id is None:
kernel_id = image.get('kernel_id', None)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
index 21f21b040..d6d964b95 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
@@ -34,7 +34,7 @@ instances_os_type = Column('os_type',
String(length=255, convert_unicode=False,
assert_unicode=None, unicode_error=None,
_warn_on_bytestring=False),
- nullable=False)
+ nullable=True)
def upgrade(migrate_engine):
@@ -43,5 +43,3 @@ def upgrade(migrate_engine):
meta.bind = migrate_engine
instances.create_column(instances_os_type)
-
-
diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py
index 05bdd172e..facd6efae 100644
--- a/nova/tests/db/fakes.py
+++ b/nova/tests/db/fakes.py
@@ -62,6 +62,7 @@ def stub_out_db_instance_api(stubs):
'mac_address': values['mac_address'],
'vcpus': type_data['vcpus'],
'local_gb': type_data['local_gb'],
+ 'os_type': values['os_type']
}
return FakeModel(base_options)
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index b9bb6d5b4..24a5698e5 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -18,6 +18,7 @@
Test suite for XenAPI
"""
+import functools
import stubout
from nova import db
@@ -41,6 +42,21 @@ from nova.tests.glance import stubs as glance_stubs
FLAGS = flags.FLAGS
+def stub_vm_utils_with_vdi_attached_here(function, should_return=True):
+ """
+ vm_utils.with_vdi_attached_here needs to be stubbed out because it
+ calls down to the filesystem to attach a vdi. This provides a
+ decorator to handle that.
+ """
+ @functools.wraps(function)
+ def decorated_function(self, *args, **kwargs):
+ orig_with_vdi_attached_here = vm_utils.with_vdi_attached_here
+ vm_utils.with_vdi_attached_here = lambda *x: should_return
+ function(self, *args, **kwargs)
+ vm_utils.with_vdi_attached_here = orig_with_vdi_attached_here
+ return decorated_function
+
+
class XenAPIVolumeTestCase(test.TestCase):
"""
Unit tests for Volume operations
@@ -62,6 +78,7 @@ class XenAPIVolumeTestCase(test.TestCase):
'ramdisk_id': 3,
'instance_type': 'm1.large',
'mac_address': 'aa:bb:cc:dd:ee:ff',
+ 'os_type': 'linux'
}
def _create_volume(self, size='0'):
@@ -219,7 +236,7 @@ class XenAPIVMTestCase(test.TestCase):
check()
- def check_vm_record(self, conn):
+ def create_vm_record(self, conn, os_type):
instances = conn.list_instances()
self.assertEquals(instances, [1])
@@ -231,28 +248,63 @@ class XenAPIVMTestCase(test.TestCase):
in xenapi_fake.get_all_records('VM').iteritems()
if not rec['is_control_domain']]
vm = vms[0]
+ self.vm_info = vm_info
+ self.vm = vm
+ def check_vm_record(self):
# Check that m1.large above turned into the right thing.
instance_type = instance_types.INSTANCE_TYPES['m1.large']
mem_kib = long(instance_type['memory_mb']) << 10
mem_bytes = str(mem_kib << 10)
vcpus = instance_type['vcpus']
- self.assertEquals(vm_info['max_mem'], mem_kib)
- self.assertEquals(vm_info['mem'], mem_kib)
- self.assertEquals(vm['memory_static_max'], mem_bytes)
- self.assertEquals(vm['memory_dynamic_max'], mem_bytes)
- self.assertEquals(vm['memory_dynamic_min'], mem_bytes)
- self.assertEquals(vm['VCPUs_max'], str(vcpus))
- self.assertEquals(vm['VCPUs_at_startup'], str(vcpus))
+ self.assertEquals(self.vm_info['max_mem'], mem_kib)
+ self.assertEquals(self.vm_info['mem'], mem_kib)
+ self.assertEquals(self.vm['memory_static_max'], mem_bytes)
+ self.assertEquals(self.vm['memory_dynamic_max'], mem_bytes)
+ self.assertEquals(self.vm['memory_dynamic_min'], mem_bytes)
+ self.assertEquals(self.vm['VCPUs_max'], str(vcpus))
+ self.assertEquals(self.vm['VCPUs_at_startup'], str(vcpus))
# Check that the VM is running according to Nova
- self.assertEquals(vm_info['state'], power_state.RUNNING)
+ self.assertEquals(self.vm_info['state'], power_state.RUNNING)
# Check that the VM is running according to XenAPI.
- self.assertEquals(vm['power_state'], 'Running')
+ self.assertEquals(self.vm['power_state'], 'Running')
+
+ def check_vm_params_for_windows(self):
+ self.assertEquals(self.vm['platform']['nx'], 'true')
+ self.assertEquals(self.vm['HVM_boot_params'], {'order': 'dc'})
+ self.assertEquals(self.vm['HVM_boot_policy'], 'BIOS order')
+
+ # check that these are not set
+ self.assertEquals(self.vm['PV_args'], '')
+ self.assertEquals(self.vm['PV_bootloader'], '')
+ self.assertEquals(self.vm['PV_kernel'], '')
+ self.assertEquals(self.vm['PV_ramdisk'], '')
+
+ def check_vm_params_for_linux(self):
+ self.assertEquals(self.vm['platform']['nx'], 'false')
+ self.assertEquals(self.vm['PV_args'], 'clocksource=jiffies')
+ self.assertEquals(self.vm['PV_bootloader'], 'pygrub')
+
+ # check that these are not set
+ self.assertEquals(self.vm['PV_kernel'], '')
+ self.assertEquals(self.vm['PV_ramdisk'], '')
+ self.assertEquals(self.vm['HVM_boot_params'], {})
+ self.assertEquals(self.vm['HVM_boot_policy'], '')
+
+ def check_vm_params_for_linux_with_external_kernel(self):
+ self.assertEquals(self.vm['platform']['nx'], 'false')
+ self.assertEquals(self.vm['PV_args'], 'root=/dev/xvda1')
+ self.assertNotEquals(self.vm['PV_kernel'], '')
+ self.assertNotEquals(self.vm['PV_ramdisk'], '')
+
+ # check that these are not set
+ self.assertEquals(self.vm['HVM_boot_params'], {})
+ self.assertEquals(self.vm['HVM_boot_policy'], '')
def _test_spawn(self, image_id, kernel_id, ramdisk_id,
- instance_type="m1.large"):
+ instance_type="m1.large", os_type="linux"):
stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests)
values = {'name': 1,
'id': 1,
@@ -263,11 +315,13 @@ class XenAPIVMTestCase(test.TestCase):
'ramdisk_id': ramdisk_id,
'instance_type': instance_type,
'mac_address': 'aa:bb:cc:dd:ee:ff',
+ 'os_type': os_type
}
conn = xenapi_conn.get_connection(False)
instance = db.instance_create(values)
conn.spawn(instance)
- self.check_vm_record(conn)
+ self.create_vm_record(conn, os_type)
+ self.check_vm_record()
def test_spawn_not_enough_memory(self):
FLAGS.xenapi_image_service = 'glance'
@@ -283,24 +337,37 @@ class XenAPIVMTestCase(test.TestCase):
FLAGS.xenapi_image_service = 'objectstore'
self._test_spawn(1, 2, 3)
+ @stub_vm_utils_with_vdi_attached_here
def test_spawn_raw_glance(self):
FLAGS.xenapi_image_service = 'glance'
self._test_spawn(glance_stubs.FakeGlance.IMAGE_RAW, None, None)
+ self.check_vm_params_for_linux()
+
+ def test_spawn_vhd_glance_linux(self):
+ FLAGS.xenapi_image_service = 'glance'
+ self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None,
+ os_type="linux")
+ self.check_vm_params_for_linux()
- def test_spawn_vhd_glance(self):
+ def test_spawn_vhd_glance_windows(self):
FLAGS.xenapi_image_service = 'glance'
- self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None)
+ self._test_spawn(glance_stubs.FakeGlance.IMAGE_VHD, None, None,
+ os_type="windows")
+ self.check_vm_params_for_windows()
def test_spawn_glance(self):
FLAGS.xenapi_image_service = 'glance'
self._test_spawn(glance_stubs.FakeGlance.IMAGE_MACHINE,
glance_stubs.FakeGlance.IMAGE_KERNEL,
glance_stubs.FakeGlance.IMAGE_RAMDISK)
+ self.check_vm_params_for_linux_with_external_kernel()
def tearDown(self):
super(XenAPIVMTestCase, self).tearDown()
self.manager.delete_project(self.project)
self.manager.delete_user(self.user)
+ self.vm_info = None
+ self.vm = None
self.stubs.UnsetAll()
def _create_instance(self):
@@ -314,7 +381,8 @@ 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',
+ 'os_type': 'linux'}
instance = db.instance_create(values)
self.conn.spawn(instance)
return instance
@@ -360,6 +428,7 @@ class XenAPIDetermineDiskImageTestCase(test.TestCase):
self.fake_instance = FakeInstance()
self.fake_instance.id = 42
+ self.fake_instance.os_type = 'linux'
def assert_disk_type(self, disk_type):
dt = vm_utils.VMHelper.determine_disk_image_type(
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 9c0bb5579..a26e391df 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -80,7 +80,8 @@ class VMHelper(HelperBase):
"""
@classmethod
- def create_vm(cls, session, instance, kernel, ramdisk, use_pv_kernel=False):
+ def create_vm(cls, session, instance, kernel, ramdisk,
+ use_pv_kernel=False):
"""Create a VM record. Returns a Deferred that gives the new
VM reference.
the use_pv_kernel flag indicates whether the guest is HVM or PV
@@ -319,7 +320,7 @@ class VMHelper(HelperBase):
'glance_host': FLAGS.glance_host,
'glance_port': FLAGS.glance_port,
'sr_path': get_sr_path(session),
- 'os_type': instance.get('os_type', 'linux')}
+ 'os_type': instance.os_type}
kwargs = {'params': pickle.dumps(params)}
task = session.async_call_plugin('glance', 'upload_vhd', kwargs)
@@ -524,7 +525,7 @@ class VMHelper(HelperBase):
Determine whether the VM will use a paravirtualized kernel or if it
will use hardware virtualization.
- 1. Objectstore (any image type):
+ 1. Objectstore (any image type):
We use plugin to figure out whether the VDI uses PV
2. Glance (VHD): then we use `os_type`, raise if not set
@@ -540,7 +541,8 @@ class VMHelper(HelperBase):
session, vdi_ref, disk_image_type, os_type)
else:
# 1. Objecstore
- return cls._determine_is_pv_objectstore(session, instance_id, vdi_ref)
+ return cls._determine_is_pv_objectstore(session, instance_id,
+ vdi_ref)
@classmethod
def _determine_is_pv_objectstore(cls, session, instance_id, vdi_ref):
@@ -564,7 +566,7 @@ class VMHelper(HelperBase):
"""
For a Glance image, determine if we need paravirtualization.
- The relevant scenarios are:
+ The relevant scenarios are:
2. Glance (VHD): then we use `os_type`, raise if not set
3. Glance (DISK_RAW): use Pygrub to figure out if pv kernel is
@@ -582,7 +584,7 @@ class VMHelper(HelperBase):
is_pv = True
elif disk_image_type == ImageType.DISK_RAW:
# 3. RAW
- is_pv = with_vdi_attached_here(session, vdi_ref, True, _is_vdi_pv)
+ is_pv = with_vdi_attached_here(session, vdi_ref, True, _is_vdi_pv)
elif disk_image_type == ImageType.DISK:
# 4. Disk
is_pv = True
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 1edf39c5b..eedb07a50 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -87,8 +87,6 @@ class VMOps(object):
vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
- os_type = instance.get('os_type', 'linux')
-
kernel = None
if instance.kernel_id:
kernel = VMHelper.fetch_image(self._session, instance.id,
@@ -99,8 +97,8 @@ class VMOps(object):
ramdisk = VMHelper.fetch_image(self._session, instance.id,
instance.ramdisk_id, user, project, ImageType.KERNEL_RAMDISK)
- use_pv_kernel = VMHelper.determine_is_pv(
- self._session, instance.id, vdi_ref, disk_image_type, os_type)
+ use_pv_kernel = VMHelper.determine_is_pv(self._session, instance.id,
+ vdi_ref, disk_image_type, instance.os_type)
vm_ref = VMHelper.create_vm(self._session, instance, kernel, ramdisk,
use_pv_kernel)
@@ -242,7 +240,7 @@ class VMOps(object):
finally:
self._destroy(instance, template_vm_ref, shutdown=False,
destroy_kernel_ramdisk=False)
-
+
logging.debug(_("Finished snapshot and upload for VM %s"), instance)
def reboot(self, instance):
--
cgit
From f36b4fe22bcb187d5f426320bbe43fcf3cb1a30a Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Fri, 4 Mar 2011 14:44:29 -0500
Subject: refactor server tests to support xml and json separately
---
nova/tests/api/openstack/test_servers.py | 103 ++++++++++++++++++++-----------
1 file changed, 68 insertions(+), 35 deletions(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index bf934113a..9e7bc3aac 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -233,12 +233,9 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
- def _personality_dict(self, path, contents):
- return {'path': path, 'contents': contents}
+ def _setup_mock_compute_api_for_personality(self):
- def _create_instance_with_personality(self, personality):
-
- class FakeComputeAPI(object):
+ class MockComputeAPI(object):
def __init__(self):
self.personality_files = None
@@ -255,28 +252,74 @@ class ServersTest(test.TestCase):
return canned_return
return stub_method
- compute_api = FakeComputeAPI()
+ compute_api = MockComputeAPI()
self.stubs.Set(nova.compute, 'API', make_stub_method(compute_api))
self.stubs.Set(nova.api.openstack.servers.Controller,
'_get_kernel_ramdisk_from_image', make_stub_method((1, 1)))
self.stubs.Set(nova.api.openstack.common,
'get_image_id_from_image_hash', make_stub_method(2))
- body = dict(server=dict(
- name='server_test', imageId=2, flavorId=2,
- metadata={},
- personality=personality))
- if personality is None:
- del body['server']['personality']
-
+ return compute_api
+
+ def _create_personality_request_dict(self, personality_files):
+ server = {}
+ server['name'] = 'new-server-test'
+ server['imageId'] = 1
+ server['flavorId'] = 1
+ if personality_files is not None:
+ personalities = []
+ for path, contents in personality_files:
+ personalities.append({'path': path, 'contents': contents})
+ server['personality'] = personalities
+ return {'server': server}
+
+ def _create_personality_request_json(self, personality_files):
+ body_dict = self._create_personality_request_dict(personality_files)
req = webob.Request.blank('/v1.0/servers')
+ req.content_type = 'application/json'
req.method = 'POST'
- req.body = json.dumps(body)
- return (req, req.get_response(fakes.wsgi_app()),
- compute_api.personality_files)
+ req.body = json.dumps(body_dict)
+ return req
+
+ def _format_xml_request_body(self, body_dict):
+ server = body_dict['server']
+ body_parts = []
+ body_parts.extend([
+ '',
+ '' % (
+ server['name'], server['imageId'], server['flavorId'])])
+ if 'metadata' in server:
+ metadata = server['metadata']
+ body_parts.append('')
+ for item in metadata.iteritems():
+ body_parts.append('%s' % item)
+ body_parts.append('')
+ if 'personality' in server:
+ personalities = server['personality']
+ body_parts.append('')
+ for item in personalities.iteritems():
+ body_parts.append('%s' % item)
+ body_parts.append('')
+ body_parts.append('')
+ return ''.join(body_parts)
+
+ def _create_personality_request_xml(self, personality_files):
+ body_dict = self._create_personality_request_dict(personality_files)
+ req = webob.Request.blank('/v1.0/servers')
+ req.content_type = 'application/xml'
+ req.method = 'POST'
+ req.body = self._format_xml_request_body(body_dict)
+ return req
+
+ def _create_instance_with_personality_json(self, personality):
+ compute_api = self._setup_mock_compute_api_for_personality()
+ request = self._create_personality_request_json(personality)
+ response = request.get_response(fakes.wsgi_app())
+ return (request, response, compute_api.personality_files)
def test_create_instance_with_no_personality(self):
request, response, personality_files = \
- self._create_instance_with_personality(personality=None)
+ self._create_instance_with_personality_json(personality=None)
self.assertEquals(response.status_int, 200)
self.assertEquals(personality_files, [])
@@ -284,18 +327,18 @@ class ServersTest(test.TestCase):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Hello, World!"\n'
b64contents = base64.b64encode(contents)
- personality = [self._personality_dict(path, b64contents)]
+ personality = [(path, b64contents)]
request, response, personality_files = \
- self._create_instance_with_personality(personality)
+ self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
self.assertEquals(personality_files, [(path, contents)])
def test_create_instance_with_personality_with_non_b64_content(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Oh no!"\n'
- personality = [self._personality_dict(path, contents)]
+ personality = [(path, contents)]
request, response, personality_files = \
- self._create_instance_with_personality(personality)
+ self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 400)
self.assertEquals(personality_files, None)
@@ -307,31 +350,21 @@ class ServersTest(test.TestCase):
]
personality = []
for path, content in files:
- personality.append(self._personality_dict(
- path, base64.b64encode(content)))
+ personality.append((path, base64.b64encode(content)))
request, response, personality_files = \
- self._create_instance_with_personality(personality)
+ self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
self.assertEquals(personality_files, files)
def test_create_instance_personality_empty_content(self):
path = '/my/file/path'
contents = ''
- personality = [self._personality_dict(path, contents)]
+ personality = [(path, contents)]
request, response, personality_files = \
- self._create_instance_with_personality(personality)
+ self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
self.assertEquals(personality_files, [(path, contents)])
- def test_create_instance_personality_not_a_list(self):
- path = '/my/file/path'
- contents = 'myfilecontents'
- personality = self._personality_dict(path, contents)
- request, response, personality_files = \
- self._create_instance_with_personality(personality)
- self.assertEquals(response.status_int, 400)
- self.assertEquals(personality_files, None)
-
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
--
cgit
From a38e6c67c37a4d3336cf1dc3717fd5612a474183 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Fri, 4 Mar 2011 14:45:31 -0500
Subject: remove xml testing infrastructure since it is not feasible to use at
present
---
nova/tests/api/openstack/test_servers.py | 31 -------------------------------
1 file changed, 31 deletions(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 9e7bc3aac..c125e6192 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -280,37 +280,6 @@ class ServersTest(test.TestCase):
req.body = json.dumps(body_dict)
return req
- def _format_xml_request_body(self, body_dict):
- server = body_dict['server']
- body_parts = []
- body_parts.extend([
- '',
- '' % (
- server['name'], server['imageId'], server['flavorId'])])
- if 'metadata' in server:
- metadata = server['metadata']
- body_parts.append('')
- for item in metadata.iteritems():
- body_parts.append('%s' % item)
- body_parts.append('')
- if 'personality' in server:
- personalities = server['personality']
- body_parts.append('')
- for item in personalities.iteritems():
- body_parts.append('%s' % item)
- body_parts.append('')
- body_parts.append('')
- return ''.join(body_parts)
-
- def _create_personality_request_xml(self, personality_files):
- body_dict = self._create_personality_request_dict(personality_files)
- req = webob.Request.blank('/v1.0/servers')
- req.content_type = 'application/xml'
- req.method = 'POST'
- req.body = self._format_xml_request_body(body_dict)
- return req
-
def _create_instance_with_personality_json(self, personality):
compute_api = self._setup_mock_compute_api_for_personality()
request = self._create_personality_request_json(personality)
--
cgit
From 1831f31af0ac21ded3535f15777bd5147c615c34 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Fri, 4 Mar 2011 11:52:18 -0800
Subject: added flatmanager unit testcases and renamed test_network.py to
test_vlan_network.py
---
nova/tests/test_flat_network.py | 276 +++++++++++++++++++++++++++++
nova/tests/test_network.py | 369 ---------------------------------------
nova/tests/test_vlan_network.py | 373 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 649 insertions(+), 369 deletions(-)
create mode 100644 nova/tests/test_flat_network.py
delete mode 100644 nova/tests/test_network.py
create mode 100644 nova/tests/test_vlan_network.py
diff --git a/nova/tests/test_flat_network.py b/nova/tests/test_flat_network.py
new file mode 100644
index 000000000..91a49920d
--- /dev/null
+++ b/nova/tests/test_flat_network.py
@@ -0,0 +1,276 @@
+# 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.
+"""
+Unit Tests for network code
+"""
+import IPy
+import os
+import unittest
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.auth import manager
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.network')
+
+
+class FlatNetworkTestCase(test.TestCase):
+ """Test cases for network code"""
+ def setUp(self):
+ super(FlatNetworkTestCase, self).setUp()
+ # NOTE(vish): if you change these flags, make sure to change the
+ # flags in the corresponding section in nova-dhcpbridge
+ self.flags(connection_type='fake',
+ fake_call=True,
+ fake_network=True)
+ self.manager = manager.AuthManager()
+ self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
+ self.projects = []
+ self.network = utils.import_object(FLAGS.network_manager)
+ self.context = context.RequestContext(project=None, user=self.user)
+ for i in range(5):
+ name = 'project%s' % i
+ project = self.manager.create_project(name, 'netuser', name)
+ self.projects.append(project)
+ # create the necessary network data for the project
+ user_context = context.RequestContext(project=self.projects[i],
+ user=self.user)
+ host = self.network.get_network_host(user_context.elevated())
+ instance_ref = self._create_instance(0)
+ self.instance_id = instance_ref['id']
+ instance_ref = self._create_instance(1)
+ self.instance2_id = instance_ref['id']
+
+ def tearDown(self):
+ # TODO(termie): this should really be instantiating clean datastores
+ # in between runs, one failure kills all the tests
+ db.instance_destroy(context.get_admin_context(), self.instance_id)
+ db.instance_destroy(context.get_admin_context(), self.instance2_id)
+ for project in self.projects:
+ self.manager.delete_project(project)
+ self.manager.delete_user(self.user)
+ super(FlatNetworkTestCase, self).tearDown()
+
+ def _create_instance(self, project_num, mac=None):
+ if not mac:
+ mac = utils.generate_mac()
+ project = self.projects[project_num]
+ self.context._project = project
+ self.context.project_id = project.id
+ return db.instance_create(self.context,
+ {'project_id': project.id,
+ 'mac_address': mac})
+
+ def _create_address(self, project_num, instance_id=None):
+ """Create an address in given project num"""
+ if instance_id is None:
+ instance_id = self.instance_id
+ self.context._project = self.projects[project_num]
+ self.context.project_id = self.projects[project_num].id
+ return self.network.allocate_fixed_ip(self.context, instance_id)
+
+ def _deallocate_address(self, project_num, address):
+ self.context._project = self.projects[project_num]
+ self.context.project_id = self.projects[project_num].id
+ self.network.deallocate_fixed_ip(self.context, address)
+
+ def test_private_ipv6(self):
+ """Make sure ipv6 is OK"""
+ if FLAGS.use_ipv6:
+ instance_ref = self._create_instance(0)
+ address = self._create_address(0, instance_ref['id'])
+ network_ref = db.project_get_network(
+ context.get_admin_context(),
+ self.context.project_id)
+ address_v6 = db.instance_get_fixed_address_v6(
+ context.get_admin_context(),
+ instance_ref['id'])
+ self.assertEqual(instance_ref['mac_address'],
+ utils.to_mac(address_v6))
+ instance_ref2 = db.fixed_ip_get_instance_v6(
+ context.get_admin_context(),
+ address_v6)
+ self.assertEqual(instance_ref['id'], instance_ref2['id'])
+ self.assertEqual(address_v6,
+ utils.to_global_ipv6(
+ network_ref['cidr_v6'],
+ instance_ref['mac_address']))
+ self._deallocate_address(0, address)
+ db.instance_destroy(context.get_admin_context(),
+ instance_ref['id'])
+
+ def test_public_network_association(self):
+ """Makes sure that we can allocate a public ip"""
+ # TODO(vish): better way of adding floating ips
+
+ self.context._project = self.projects[0]
+ self.context.project_id = self.projects[0].id
+ pubnet = IPy.IP(flags.FLAGS.floating_range)
+ address = str(pubnet[0])
+ try:
+ db.floating_ip_get_by_address(context.get_admin_context(), address)
+ except exception.NotFound:
+ db.floating_ip_create(context.get_admin_context(),
+ {'address': address,
+ 'host': FLAGS.host})
+
+ self.assertRaises(NotImplementedError,
+ self.network.allocate_floating_ip,
+ self.context, self.projects[0].id)
+
+ fix_addr = self._create_address(0)
+ float_addr = address
+ self.assertRaises(NotImplementedError,
+ self.network.associate_floating_ip,
+ self.context, float_addr, fix_addr)
+
+ address = db.instance_get_floating_address(context.get_admin_context(),
+ self.instance_id)
+ self.assertEqual(address, None)
+
+ self.assertRaises(NotImplementedError,
+ self.network.disassociate_floating_ip,
+ self.context, float_addr)
+
+ address = db.instance_get_floating_address(context.get_admin_context(),
+ self.instance_id)
+ self.assertEqual(address, None)
+
+ self.assertRaises(NotImplementedError,
+ self.network.deallocate_floating_ip,
+ self.context, float_addr)
+
+ self.network.deallocate_fixed_ip(self.context, fix_addr)
+ db.floating_ip_destroy(context.get_admin_context(), float_addr)
+
+ def test_allocate_deallocate_fixed_ip(self):
+ """Makes sure that we can allocate and deallocate a fixed ip"""
+ address = self._create_address(0)
+ self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
+ self._deallocate_address(0, address)
+
+ # check if the fixed ip address is really deallocated
+ self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
+
+ def test_side_effects(self):
+ """Ensures allocating and releasing has no side effects"""
+ address = self._create_address(0)
+ address2 = self._create_address(1, self.instance2_id)
+
+ self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
+ self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
+
+ self._deallocate_address(0, address)
+ self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
+
+ # First address release shouldn't affect the second
+ self.assertTrue(is_allocated_in_project(address2, self.projects[0].id))
+
+ self._deallocate_address(1, address2)
+ self.assertFalse(is_allocated_in_project(address2,
+ self.projects[1].id))
+
+ def test_ips_are_reused(self):
+ """Makes sure that ip addresses that are deallocated get reused"""
+ address = self._create_address(0)
+ self.network.deallocate_fixed_ip(self.context, address)
+
+ address2 = self._create_address(0)
+ self.assertEqual(address, address2)
+
+ self.network.deallocate_fixed_ip(self.context, address2)
+
+ def test_available_ips(self):
+ """Make sure the number of available ips for the network is correct
+
+ The number of available IP addresses depends on the test
+ environment's setup.
+
+ Network size is set in test fixture's setUp method.
+
+ There are ips reserved at the bottom and top of the range.
+ services (network, gateway, CloudPipe, broadcast)
+ """
+ network = db.project_get_network(context.get_admin_context(),
+ self.projects[0].id)
+ net_size = flags.FLAGS.network_size
+ admin_context = context.get_admin_context()
+ total_ips = (db.network_count_available_ips(admin_context,
+ network['id']) +
+ db.network_count_reserved_ips(admin_context,
+ network['id']) +
+ db.network_count_allocated_ips(admin_context,
+ network['id']))
+ self.assertEqual(total_ips, net_size)
+
+ def test_too_many_addresses(self):
+ """Test for a NoMoreAddresses exception when all fixed ips are used.
+ """
+ admin_context = context.get_admin_context()
+ network = db.project_get_network(admin_context, self.projects[0].id)
+ num_available_ips = db.network_count_available_ips(admin_context,
+ network['id'])
+ addresses = []
+ instance_ids = []
+ for i in range(num_available_ips):
+ instance_ref = self._create_instance(0)
+ instance_ids.append(instance_ref['id'])
+ address = self._create_address(0, instance_ref['id'])
+ addresses.append(address)
+
+ ip_count = db.network_count_available_ips(context.get_admin_context(),
+ network['id'])
+ self.assertEqual(ip_count, 0)
+ self.assertRaises(db.NoMoreAddresses,
+ self.network.allocate_fixed_ip,
+ self.context,
+ 'foo')
+
+ for i in range(num_available_ips):
+ self.network.deallocate_fixed_ip(self.context, addresses[i])
+ db.instance_destroy(context.get_admin_context(), instance_ids[i])
+ ip_count = db.network_count_available_ips(context.get_admin_context(),
+ network['id'])
+ self.assertEqual(ip_count, num_available_ips)
+
+ def run(self, result=None):
+ if(FLAGS.network_manager == 'nova.network.manager.FlatManager'):
+ super(FlatNetworkTestCase, self).run(result)
+
+
+def is_allocated_in_project(address, project_id):
+ """Returns true if address is in specified project"""
+ #project_net = db.project_get_network(context.get_admin_context(),
+ # project_id)
+ project_net = db.network_get_by_bridge(context.get_admin_context(),
+ FLAGS.flat_network_bridge)
+ network = db.fixed_ip_get_network(context.get_admin_context(), address)
+ instance = db.fixed_ip_get_instance(context.get_admin_context(), address)
+ # instance exists until release
+ return instance is not None and network['id'] == project_net['id']
+
+
+def binpath(script):
+ """Returns the absolute path to a script in bin"""
+ return os.path.abspath(os.path.join(__file__, "../../../bin", script))
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
deleted file mode 100644
index ce1c77210..000000000
--- a/nova/tests/test_network.py
+++ /dev/null
@@ -1,369 +0,0 @@
-# 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.
-"""
-Unit Tests for network code
-"""
-import IPy
-import os
-
-from nova import context
-from nova import db
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import test
-from nova import utils
-from nova.auth import manager
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger('nova.tests.network')
-
-
-class NetworkTestCase(test.TestCase):
- """Test cases for network code"""
- def setUp(self):
- super(NetworkTestCase, self).setUp()
- # NOTE(vish): if you change these flags, make sure to change the
- # flags in the corresponding section in nova-dhcpbridge
- self.flags(connection_type='fake',
- fake_call=True,
- fake_network=True)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
- self.projects = []
- self.network = utils.import_object(FLAGS.network_manager)
- self.context = context.RequestContext(project=None, user=self.user)
- for i in range(FLAGS.num_networks):
- name = 'project%s' % i
- project = self.manager.create_project(name, 'netuser', name)
- self.projects.append(project)
- # create the necessary network data for the project
- user_context = context.RequestContext(project=self.projects[i],
- user=self.user)
- host = self.network.get_network_host(user_context.elevated())
- instance_ref = self._create_instance(0)
- self.instance_id = instance_ref['id']
- instance_ref = self._create_instance(1)
- self.instance2_id = instance_ref['id']
-
- def tearDown(self):
- # TODO(termie): this should really be instantiating clean datastores
- # in between runs, one failure kills all the tests
- db.instance_destroy(context.get_admin_context(), self.instance_id)
- db.instance_destroy(context.get_admin_context(), self.instance2_id)
- for project in self.projects:
- self.manager.delete_project(project)
- self.manager.delete_user(self.user)
- super(NetworkTestCase, self).tearDown()
-
- def _create_instance(self, project_num, mac=None):
- if not mac:
- mac = utils.generate_mac()
- project = self.projects[project_num]
- self.context._project = project
- self.context.project_id = project.id
- return db.instance_create(self.context,
- {'project_id': project.id,
- 'mac_address': mac})
-
- def _create_address(self, project_num, instance_id=None):
- """Create an address in given project num"""
- if instance_id is None:
- instance_id = self.instance_id
- self.context._project = self.projects[project_num]
- self.context.project_id = self.projects[project_num].id
- return self.network.allocate_fixed_ip(self.context, instance_id)
-
- def _deallocate_address(self, project_num, address):
- self.context._project = self.projects[project_num]
- self.context.project_id = self.projects[project_num].id
- self.network.deallocate_fixed_ip(self.context, address)
-
- def test_private_ipv6(self):
- """Make sure ipv6 is OK"""
- if FLAGS.use_ipv6:
- instance_ref = self._create_instance(0)
- address = self._create_address(0, instance_ref['id'])
- network_ref = db.project_get_network(
- context.get_admin_context(),
- self.context.project_id)
- address_v6 = db.instance_get_fixed_address_v6(
- context.get_admin_context(),
- instance_ref['id'])
- self.assertEqual(instance_ref['mac_address'],
- utils.to_mac(address_v6))
- instance_ref2 = db.fixed_ip_get_instance_v6(
- context.get_admin_context(),
- address_v6)
- self.assertEqual(instance_ref['id'], instance_ref2['id'])
- self.assertEqual(address_v6,
- utils.to_global_ipv6(
- network_ref['cidr_v6'],
- instance_ref['mac_address']))
- self._deallocate_address(0, address)
- db.instance_destroy(context.get_admin_context(),
- instance_ref['id'])
-
- def test_public_network_association(self):
- """Makes sure that we can allocaate a public ip"""
- # TODO(vish): better way of adding floating ips
- self.context._project = self.projects[0]
- self.context.project_id = self.projects[0].id
- pubnet = IPy.IP(flags.FLAGS.floating_range)
- address = str(pubnet[0])
- try:
- db.floating_ip_get_by_address(context.get_admin_context(), address)
- except exception.NotFound:
- db.floating_ip_create(context.get_admin_context(),
- {'address': address,
- 'host': FLAGS.host})
- float_addr = self.network.allocate_floating_ip(self.context,
- self.projects[0].id)
- fix_addr = self._create_address(0)
- lease_ip(fix_addr)
- self.assertEqual(float_addr, str(pubnet[0]))
- self.network.associate_floating_ip(self.context, float_addr, fix_addr)
- address = db.instance_get_floating_address(context.get_admin_context(),
- self.instance_id)
- self.assertEqual(address, float_addr)
- self.network.disassociate_floating_ip(self.context, float_addr)
- address = db.instance_get_floating_address(context.get_admin_context(),
- self.instance_id)
- self.assertEqual(address, None)
- self.network.deallocate_floating_ip(self.context, float_addr)
- self.network.deallocate_fixed_ip(self.context, fix_addr)
- release_ip(fix_addr)
- db.floating_ip_destroy(context.get_admin_context(), float_addr)
-
- def test_allocate_deallocate_fixed_ip(self):
- """Makes sure that we can allocate and deallocate a fixed ip"""
- address = self._create_address(0)
- self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
- lease_ip(address)
- self._deallocate_address(0, address)
-
- # Doesn't go away until it's dhcp released
- self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
-
- release_ip(address)
- self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
-
- def test_side_effects(self):
- """Ensures allocating and releasing has no side effects"""
- address = self._create_address(0)
- address2 = self._create_address(1, self.instance2_id)
-
- self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
- self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
- self.assertFalse(is_allocated_in_project(address, self.projects[1].id))
-
- # Addresses are allocated before they're issued
- lease_ip(address)
- lease_ip(address2)
-
- self._deallocate_address(0, address)
- release_ip(address)
- self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
-
- # First address release shouldn't affect the second
- self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
-
- self._deallocate_address(1, address2)
- release_ip(address2)
- self.assertFalse(is_allocated_in_project(address2,
- self.projects[1].id))
-
- def test_subnet_edge(self):
- """Makes sure that private ips don't overlap"""
- first = self._create_address(0)
- lease_ip(first)
- instance_ids = []
- for i in range(1, FLAGS.num_networks):
- instance_ref = self._create_instance(i, mac=utils.generate_mac())
- instance_ids.append(instance_ref['id'])
- address = self._create_address(i, instance_ref['id'])
- instance_ref = self._create_instance(i, mac=utils.generate_mac())
- instance_ids.append(instance_ref['id'])
- address2 = self._create_address(i, instance_ref['id'])
- instance_ref = self._create_instance(i, mac=utils.generate_mac())
- instance_ids.append(instance_ref['id'])
- address3 = self._create_address(i, instance_ref['id'])
- lease_ip(address)
- lease_ip(address2)
- lease_ip(address3)
- self.context._project = self.projects[i]
- self.context.project_id = self.projects[i].id
- self.assertFalse(is_allocated_in_project(address,
- self.projects[0].id))
- self.assertFalse(is_allocated_in_project(address2,
- self.projects[0].id))
- self.assertFalse(is_allocated_in_project(address3,
- self.projects[0].id))
- self.network.deallocate_fixed_ip(self.context, address)
- self.network.deallocate_fixed_ip(self.context, address2)
- self.network.deallocate_fixed_ip(self.context, address3)
- release_ip(address)
- release_ip(address2)
- release_ip(address3)
- for instance_id in instance_ids:
- db.instance_destroy(context.get_admin_context(), instance_id)
- self.context._project = self.projects[0]
- self.context.project_id = self.projects[0].id
- self.network.deallocate_fixed_ip(self.context, first)
- self._deallocate_address(0, first)
- release_ip(first)
-
- def test_vpn_ip_and_port_looks_valid(self):
- """Ensure the vpn ip and port are reasonable"""
- self.assert_(self.projects[0].vpn_ip)
- self.assert_(self.projects[0].vpn_port >= FLAGS.vpn_start)
- self.assert_(self.projects[0].vpn_port <= FLAGS.vpn_start +
- FLAGS.num_networks)
-
- def test_too_many_networks(self):
- """Ensure error is raised if we run out of networks"""
- projects = []
- networks_left = (FLAGS.num_networks -
- db.network_count(context.get_admin_context()))
- for i in range(networks_left):
- project = self.manager.create_project('many%s' % i, self.user)
- projects.append(project)
- db.project_get_network(context.get_admin_context(), project.id)
- project = self.manager.create_project('last', self.user)
- projects.append(project)
- self.assertRaises(db.NoMoreNetworks,
- db.project_get_network,
- context.get_admin_context(),
- project.id)
- for project in projects:
- self.manager.delete_project(project)
-
- def test_ips_are_reused(self):
- """Makes sure that ip addresses that are deallocated get reused"""
- address = self._create_address(0)
- lease_ip(address)
- self.network.deallocate_fixed_ip(self.context, address)
- release_ip(address)
-
- address2 = self._create_address(0)
- self.assertEqual(address, address2)
- lease_ip(address)
- self.network.deallocate_fixed_ip(self.context, address2)
- release_ip(address)
-
- def test_available_ips(self):
- """Make sure the number of available ips for the network is correct
-
- The number of available IP addresses depends on the test
- environment's setup.
-
- Network size is set in test fixture's setUp method.
-
- There are ips reserved at the bottom and top of the range.
- services (network, gateway, CloudPipe, broadcast)
- """
- network = db.project_get_network(context.get_admin_context(),
- self.projects[0].id)
- net_size = flags.FLAGS.network_size
- admin_context = context.get_admin_context()
- total_ips = (db.network_count_available_ips(admin_context,
- network['id']) +
- db.network_count_reserved_ips(admin_context,
- network['id']) +
- db.network_count_allocated_ips(admin_context,
- network['id']))
- self.assertEqual(total_ips, net_size)
-
- def test_too_many_addresses(self):
- """Test for a NoMoreAddresses exception when all fixed ips are used.
- """
- admin_context = context.get_admin_context()
- network = db.project_get_network(admin_context, self.projects[0].id)
- num_available_ips = db.network_count_available_ips(admin_context,
- network['id'])
- addresses = []
- instance_ids = []
- for i in range(num_available_ips):
- instance_ref = self._create_instance(0)
- instance_ids.append(instance_ref['id'])
- address = self._create_address(0, instance_ref['id'])
- addresses.append(address)
- lease_ip(address)
-
- ip_count = db.network_count_available_ips(context.get_admin_context(),
- network['id'])
- self.assertEqual(ip_count, 0)
- self.assertRaises(db.NoMoreAddresses,
- self.network.allocate_fixed_ip,
- self.context,
- 'foo')
-
- for i in range(num_available_ips):
- self.network.deallocate_fixed_ip(self.context, addresses[i])
- release_ip(addresses[i])
- db.instance_destroy(context.get_admin_context(), instance_ids[i])
- ip_count = db.network_count_available_ips(context.get_admin_context(),
- network['id'])
- self.assertEqual(ip_count, num_available_ips)
-
-
-def is_allocated_in_project(address, project_id):
- """Returns true if address is in specified project"""
- project_net = db.project_get_network(context.get_admin_context(),
- project_id)
- network = db.fixed_ip_get_network(context.get_admin_context(), address)
- instance = db.fixed_ip_get_instance(context.get_admin_context(), address)
- # instance exists until release
- return instance is not None and network['id'] == project_net['id']
-
-
-def binpath(script):
- """Returns the absolute path to a script in bin"""
- return os.path.abspath(os.path.join(__file__, "../../../bin", script))
-
-
-def lease_ip(private_ip):
- """Run add command on dhcpbridge"""
- network_ref = db.fixed_ip_get_network(context.get_admin_context(),
- private_ip)
- instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
- private_ip)
- cmd = "%s add %s %s fake" % (binpath('nova-dhcpbridge'),
- instance_ref['mac_address'],
- private_ip)
- env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
- 'TESTING': '1',
- 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
- (out, err) = utils.execute(cmd, addl_env=env)
- LOG.debug("ISSUE_IP: %s, %s ", out, err)
-
-
-def release_ip(private_ip):
- """Run del command on dhcpbridge"""
- network_ref = db.fixed_ip_get_network(context.get_admin_context(),
- private_ip)
- instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
- private_ip)
- cmd = "%s del %s %s fake" % (binpath('nova-dhcpbridge'),
- instance_ref['mac_address'],
- private_ip)
- env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
- 'TESTING': '1',
- 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
- (out, err) = utils.execute(cmd, addl_env=env)
- LOG.debug("RELEASE_IP: %s, %s ", out, err)
diff --git a/nova/tests/test_vlan_network.py b/nova/tests/test_vlan_network.py
new file mode 100644
index 000000000..be39313bb
--- /dev/null
+++ b/nova/tests/test_vlan_network.py
@@ -0,0 +1,373 @@
+# 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.
+"""
+Unit Tests for network code
+"""
+import IPy
+import os
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.auth import manager
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.network')
+
+
+class VlanNetworkTestCase(test.TestCase):
+ """Test cases for network code"""
+ def setUp(self):
+ super(VlanNetworkTestCase, self).setUp()
+ # NOTE(vish): if you change these flags, make sure to change the
+ # flags in the corresponding section in nova-dhcpbridge
+ self.flags(connection_type='fake',
+ fake_call=True,
+ fake_network=True)
+ self.manager = manager.AuthManager()
+ self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
+ self.projects = []
+ self.network = utils.import_object(FLAGS.network_manager)
+ self.context = context.RequestContext(project=None, user=self.user)
+ for i in range(FLAGS.num_networks):
+ name = 'project%s' % i
+ project = self.manager.create_project(name, 'netuser', name)
+ self.projects.append(project)
+ # create the necessary network data for the project
+ user_context = context.RequestContext(project=self.projects[i],
+ user=self.user)
+ host = self.network.get_network_host(user_context.elevated())
+ instance_ref = self._create_instance(0)
+ self.instance_id = instance_ref['id']
+ instance_ref = self._create_instance(1)
+ self.instance2_id = instance_ref['id']
+
+ def tearDown(self):
+ # TODO(termie): this should really be instantiating clean datastores
+ # in between runs, one failure kills all the tests
+ db.instance_destroy(context.get_admin_context(), self.instance_id)
+ db.instance_destroy(context.get_admin_context(), self.instance2_id)
+ for project in self.projects:
+ self.manager.delete_project(project)
+ self.manager.delete_user(self.user)
+ super(VlanNetworkTestCase, self).tearDown()
+
+ def run(self, result=None):
+ if(FLAGS.network_manager == 'nova.network.manager.VlanManager'):
+ super(VlanNetworkTestCase, self).run(result)
+
+ def _create_instance(self, project_num, mac=None):
+ if not mac:
+ mac = utils.generate_mac()
+ project = self.projects[project_num]
+ self.context._project = project
+ self.context.project_id = project.id
+ return db.instance_create(self.context,
+ {'project_id': project.id,
+ 'mac_address': mac})
+
+ def _create_address(self, project_num, instance_id=None):
+ """Create an address in given project num"""
+ if instance_id is None:
+ instance_id = self.instance_id
+ self.context._project = self.projects[project_num]
+ self.context.project_id = self.projects[project_num].id
+ return self.network.allocate_fixed_ip(self.context, instance_id)
+
+ def _deallocate_address(self, project_num, address):
+ self.context._project = self.projects[project_num]
+ self.context.project_id = self.projects[project_num].id
+ self.network.deallocate_fixed_ip(self.context, address)
+
+ def test_private_ipv6(self):
+ """Make sure ipv6 is OK"""
+ if FLAGS.use_ipv6:
+ instance_ref = self._create_instance(0)
+ address = self._create_address(0, instance_ref['id'])
+ network_ref = db.project_get_network(
+ context.get_admin_context(),
+ self.context.project_id)
+ address_v6 = db.instance_get_fixed_address_v6(
+ context.get_admin_context(),
+ instance_ref['id'])
+ self.assertEqual(instance_ref['mac_address'],
+ utils.to_mac(address_v6))
+ instance_ref2 = db.fixed_ip_get_instance_v6(
+ context.get_admin_context(),
+ address_v6)
+ self.assertEqual(instance_ref['id'], instance_ref2['id'])
+ self.assertEqual(address_v6,
+ utils.to_global_ipv6(
+ network_ref['cidr_v6'],
+ instance_ref['mac_address']))
+ self._deallocate_address(0, address)
+ db.instance_destroy(context.get_admin_context(),
+ instance_ref['id'])
+
+ def test_public_network_association(self):
+ """Makes sure that we can allocaate a public ip"""
+ # TODO(vish): better way of adding floating ips
+ self.context._project = self.projects[0]
+ self.context.project_id = self.projects[0].id
+ pubnet = IPy.IP(flags.FLAGS.floating_range)
+ address = str(pubnet[0])
+ try:
+ db.floating_ip_get_by_address(context.get_admin_context(), address)
+ except exception.NotFound:
+ db.floating_ip_create(context.get_admin_context(),
+ {'address': address,
+ 'host': FLAGS.host})
+ float_addr = self.network.allocate_floating_ip(self.context,
+ self.projects[0].id)
+ fix_addr = self._create_address(0)
+ lease_ip(fix_addr)
+ self.assertEqual(float_addr, str(pubnet[0]))
+ self.network.associate_floating_ip(self.context, float_addr, fix_addr)
+ address = db.instance_get_floating_address(context.get_admin_context(),
+ self.instance_id)
+ self.assertEqual(address, float_addr)
+ self.network.disassociate_floating_ip(self.context, float_addr)
+ address = db.instance_get_floating_address(context.get_admin_context(),
+ self.instance_id)
+ self.assertEqual(address, None)
+ self.network.deallocate_floating_ip(self.context, float_addr)
+ self.network.deallocate_fixed_ip(self.context, fix_addr)
+ release_ip(fix_addr)
+ db.floating_ip_destroy(context.get_admin_context(), float_addr)
+
+ def test_allocate_deallocate_fixed_ip(self):
+ """Makes sure that we can allocate and deallocate a fixed ip"""
+ address = self._create_address(0)
+ self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
+ lease_ip(address)
+ self._deallocate_address(0, address)
+
+ # Doesn't go away until it's dhcp released
+ self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
+
+ release_ip(address)
+ self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
+
+ def test_side_effects(self):
+ """Ensures allocating and releasing has no side effects"""
+ address = self._create_address(0)
+ address2 = self._create_address(1, self.instance2_id)
+
+ self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
+ self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
+ self.assertFalse(is_allocated_in_project(address, self.projects[1].id))
+
+ # Addresses are allocated before they're issued
+ lease_ip(address)
+ lease_ip(address2)
+
+ self._deallocate_address(0, address)
+ release_ip(address)
+ self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
+
+ # First address release shouldn't affect the second
+ self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
+
+ self._deallocate_address(1, address2)
+ release_ip(address2)
+ self.assertFalse(is_allocated_in_project(address2,
+ self.projects[1].id))
+
+ def test_subnet_edge(self):
+ """Makes sure that private ips don't overlap"""
+ first = self._create_address(0)
+ lease_ip(first)
+ instance_ids = []
+ for i in range(1, FLAGS.num_networks):
+ instance_ref = self._create_instance(i, mac=utils.generate_mac())
+ instance_ids.append(instance_ref['id'])
+ address = self._create_address(i, instance_ref['id'])
+ instance_ref = self._create_instance(i, mac=utils.generate_mac())
+ instance_ids.append(instance_ref['id'])
+ address2 = self._create_address(i, instance_ref['id'])
+ instance_ref = self._create_instance(i, mac=utils.generate_mac())
+ instance_ids.append(instance_ref['id'])
+ address3 = self._create_address(i, instance_ref['id'])
+ lease_ip(address)
+ lease_ip(address2)
+ lease_ip(address3)
+ self.context._project = self.projects[i]
+ self.context.project_id = self.projects[i].id
+ self.assertFalse(is_allocated_in_project(address,
+ self.projects[0].id))
+ self.assertFalse(is_allocated_in_project(address2,
+ self.projects[0].id))
+ self.assertFalse(is_allocated_in_project(address3,
+ self.projects[0].id))
+ self.network.deallocate_fixed_ip(self.context, address)
+ self.network.deallocate_fixed_ip(self.context, address2)
+ self.network.deallocate_fixed_ip(self.context, address3)
+ release_ip(address)
+ release_ip(address2)
+ release_ip(address3)
+ for instance_id in instance_ids:
+ db.instance_destroy(context.get_admin_context(), instance_id)
+ self.context._project = self.projects[0]
+ self.context.project_id = self.projects[0].id
+ self.network.deallocate_fixed_ip(self.context, first)
+ self._deallocate_address(0, first)
+ release_ip(first)
+
+ def test_vpn_ip_and_port_looks_valid(self):
+ """Ensure the vpn ip and port are reasonable"""
+ self.assert_(self.projects[0].vpn_ip)
+ self.assert_(self.projects[0].vpn_port >= FLAGS.vpn_start)
+ self.assert_(self.projects[0].vpn_port <= FLAGS.vpn_start +
+ FLAGS.num_networks)
+
+ def test_too_many_networks(self):
+ """Ensure error is raised if we run out of networks"""
+ projects = []
+ networks_left = (FLAGS.num_networks -
+ db.network_count(context.get_admin_context()))
+ for i in range(networks_left):
+ project = self.manager.create_project('many%s' % i, self.user)
+ projects.append(project)
+ db.project_get_network(context.get_admin_context(), project.id)
+ project = self.manager.create_project('last', self.user)
+ projects.append(project)
+ self.assertRaises(db.NoMoreNetworks,
+ db.project_get_network,
+ context.get_admin_context(),
+ project.id)
+ for project in projects:
+ self.manager.delete_project(project)
+
+ def test_ips_are_reused(self):
+ """Makes sure that ip addresses that are deallocated get reused"""
+ address = self._create_address(0)
+ lease_ip(address)
+ self.network.deallocate_fixed_ip(self.context, address)
+ release_ip(address)
+
+ address2 = self._create_address(0)
+ self.assertEqual(address, address2)
+ lease_ip(address)
+ self.network.deallocate_fixed_ip(self.context, address2)
+ release_ip(address)
+
+ def test_available_ips(self):
+ """Make sure the number of available ips for the network is correct
+
+ The number of available IP addresses depends on the test
+ environment's setup.
+
+ Network size is set in test fixture's setUp method.
+
+ There are ips reserved at the bottom and top of the range.
+ services (network, gateway, CloudPipe, broadcast)
+ """
+ network = db.project_get_network(context.get_admin_context(),
+ self.projects[0].id)
+ net_size = flags.FLAGS.network_size
+ admin_context = context.get_admin_context()
+ total_ips = (db.network_count_available_ips(admin_context,
+ network['id']) +
+ db.network_count_reserved_ips(admin_context,
+ network['id']) +
+ db.network_count_allocated_ips(admin_context,
+ network['id']))
+ self.assertEqual(total_ips, net_size)
+
+ def test_too_many_addresses(self):
+ """Test for a NoMoreAddresses exception when all fixed ips are used.
+ """
+ admin_context = context.get_admin_context()
+ network = db.project_get_network(admin_context, self.projects[0].id)
+ num_available_ips = db.network_count_available_ips(admin_context,
+ network['id'])
+ addresses = []
+ instance_ids = []
+ for i in range(num_available_ips):
+ instance_ref = self._create_instance(0)
+ instance_ids.append(instance_ref['id'])
+ address = self._create_address(0, instance_ref['id'])
+ addresses.append(address)
+ lease_ip(address)
+
+ ip_count = db.network_count_available_ips(context.get_admin_context(),
+ network['id'])
+ self.assertEqual(ip_count, 0)
+ self.assertRaises(db.NoMoreAddresses,
+ self.network.allocate_fixed_ip,
+ self.context,
+ 'foo')
+
+ for i in range(num_available_ips):
+ self.network.deallocate_fixed_ip(self.context, addresses[i])
+ release_ip(addresses[i])
+ db.instance_destroy(context.get_admin_context(), instance_ids[i])
+ ip_count = db.network_count_available_ips(context.get_admin_context(),
+ network['id'])
+ self.assertEqual(ip_count, num_available_ips)
+
+
+def is_allocated_in_project(address, project_id):
+ """Returns true if address is in specified project"""
+ project_net = db.project_get_network(context.get_admin_context(),
+ project_id)
+ network = db.fixed_ip_get_network(context.get_admin_context(), address)
+ instance = db.fixed_ip_get_instance(context.get_admin_context(), address)
+ # instance exists until release
+ return instance is not None and network['id'] == project_net['id']
+
+
+def binpath(script):
+ """Returns the absolute path to a script in bin"""
+ return os.path.abspath(os.path.join(__file__, "../../../bin", script))
+
+
+def lease_ip(private_ip):
+ """Run add command on dhcpbridge"""
+ network_ref = db.fixed_ip_get_network(context.get_admin_context(),
+ private_ip)
+ instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
+ private_ip)
+ cmd = "%s add %s %s fake" % (binpath('nova-dhcpbridge'),
+ instance_ref['mac_address'],
+ private_ip)
+ env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
+ 'TESTING': '1',
+ 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
+ (out, err) = utils.execute(cmd, addl_env=env)
+ LOG.debug("ISSUE_IP: %s, %s ", out, err)
+
+
+def release_ip(private_ip):
+ """Run del command on dhcpbridge"""
+ network_ref = db.fixed_ip_get_network(context.get_admin_context(),
+ private_ip)
+ instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
+ private_ip)
+ cmd = "%s del %s %s fake" % (binpath('nova-dhcpbridge'),
+ instance_ref['mac_address'],
+ private_ip)
+ env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
+ 'TESTING': '1',
+ 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
+ (out, err) = utils.execute(cmd, addl_env=env)
+ LOG.debug("RELEASE_IP: %s, %s ", out, err)
--
cgit
From 10668b87f46a1fb5d039f6e7d7a7a55b89d7602a Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Fri, 4 Mar 2011 17:04:41 -0500
Subject: respond well if personality attribute is incomplete
---
nova/api/openstack/servers.py | 3 ++
nova/tests/api/openstack/test_servers.py | 48 ++++++++++++++++++++++++++++----
2 files changed, 46 insertions(+), 5 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 8b7b20b92..7c620dbc6 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -153,6 +153,9 @@ class Controller(wsgi.Controller):
try:
path = item['path']
contents = item['contents']
+ except KeyError, key:
+ expl = 'Bad personality format: missing %s' % key
+ raise exc.HTTPBadRequest(explanation=expl)
except TypeError:
raise exc.HTTPBadRequest(explanation='Bad personality format')
try:
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index c125e6192..8fb5a9aec 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -272,19 +272,24 @@ class ServersTest(test.TestCase):
server['personality'] = personalities
return {'server': server}
- def _create_personality_request_json(self, personality_files):
- body_dict = self._create_personality_request_dict(personality_files)
+ def _get_create_request_json(self, body_dict):
req = webob.Request.blank('/v1.0/servers')
req.content_type = 'application/json'
req.method = 'POST'
req.body = json.dumps(body_dict)
return req
- def _create_instance_with_personality_json(self, personality):
+ def _run_create_instance_with_mock_compute_api(self, request):
compute_api = self._setup_mock_compute_api_for_personality()
- request = self._create_personality_request_json(personality)
response = request.get_response(fakes.wsgi_app())
- return (request, response, compute_api.personality_files)
+ return compute_api, response
+
+ def _create_instance_with_personality_json(self, personality):
+ body_dict = self._create_personality_request_dict(personality)
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ return request, response, compute_api.personality_files
def test_create_instance_with_no_personality(self):
request, response, personality_files = \
@@ -302,6 +307,39 @@ class ServersTest(test.TestCase):
self.assertEquals(response.status_int, 200)
self.assertEquals(personality_files, [(path, contents)])
+ def test_create_instance_with_personality_no_path(self):
+ personality = [('/remove/this/path',
+ base64.b64encode('my\n\file\ncontents'))]
+ body_dict = self._create_personality_request_dict(personality)
+ del body_dict['server']['personality'][0]['path']
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(compute_api.personality_files, None)
+
+ def test_create_instance_with_personality_no_contents(self):
+ personality = [('/test/path',
+ base64.b64encode('remove\nthese\ncontents'))]
+ body_dict = self._create_personality_request_dict(personality)
+ del body_dict['server']['personality'][0]['contents']
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(compute_api.personality_files, None)
+
+ def test_create_instance_with_personality_not_a_list(self):
+ personality = [('/test/path', base64.b64encode('test\ncontents\n'))]
+ body_dict = self._create_personality_request_dict(personality)
+ body_dict['server']['personality'] = \
+ body_dict['server']['personality'][0]
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(compute_api.personality_files, None)
+
def test_create_instance_with_personality_with_non_b64_content(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Oh no!"\n'
--
cgit
From e63cd9d5dc856f81477cf6c0e6c77ed7d1f4d70c Mon Sep 17 00:00:00 2001
From: Cory Wright
Date: Fri, 4 Mar 2011 22:17:53 +0000
Subject: * os_type is no longer `not null`
---
.../versions/007_add_os_type_to_instances.py | 45 -----------------
.../versions/009_add_os_type_to_instances.py | 56 ++++++++++++++++++++++
nova/virt/xenapi/vm_utils.py | 8 ++--
3 files changed, 60 insertions(+), 49 deletions(-)
delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
deleted file mode 100644
index d6d964b95..000000000
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_os_type_to_instances.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# 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.
-
-from sqlalchemy import *
-from migrate import *
-
-from nova import log as logging
-
-
-meta = MetaData()
-
-instances = Table('instances', meta,
- Column('id', Integer(), primary_key=True, nullable=False),
- )
-
-# FIXME(dubs) should this be not null? Maybe create as nullable, then
-# populate all existing rows with 'linux', then adding not null constraint.
-instances_os_type = Column('os_type',
- String(length=255, convert_unicode=False,
- assert_unicode=None, unicode_error=None,
- _warn_on_bytestring=False),
- nullable=True)
-
-
-def upgrade(migrate_engine):
- # Upgrade operations go here. Don't create your own engine;
- # bind migrate_engine to your metadata
- meta.bind = migrate_engine
-
- instances.create_column(instances_os_type)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
new file mode 100644
index 000000000..a50f31e16
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
@@ -0,0 +1,56 @@
+# 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.
+
+from sqlalchemy import *
+from sqlalchemy.sql import text
+from migrate import *
+
+from nova import log as logging
+
+
+meta = MetaData()
+
+instances = Table('instances', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+# FIXME(dubs) should this be not null? Maybe create as nullable, then
+# populate all existing rows with 'linux', then adding not null constraint.
+instances_os_type = Column('os_type',
+ String(length=255, convert_unicode=False,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False),
+ nullable=True)
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ instances.create_column(instances_os_type)
+ migrate_engine.execute(instances.update()\
+ .where(instances.c.os_type==None)\
+ .values(os_type='linux'))
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ instances.drop_column('os_type')
+
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 7bff81b66..150824400 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -117,7 +117,7 @@ class VMHelper(HelperBase):
'memory_target': mem,
'name_description': '',
'name_label': instance.name,
-# 'other_config': {'allowvssprovider': False},
+ 'other_config': {'allowvssprovider': False},
'other_config': {},
'PCI_bus': '',
'platform': {'acpi': 'true', 'apic': 'true', 'pae': 'true',
@@ -313,14 +313,14 @@ class VMHelper(HelperBase):
logging.debug(_("Asking xapi to upload %(vdi_uuids)s as"
" ID %(image_id)s") % locals())
- # TODO(dubs): os_type is currently defaulting to linux, we actually
- # want to make this a NOT NULL column and require it to be specified.
+ os_type = instance.os_type and instance.os_type or 'linux'
+
params = {'vdi_uuids': vdi_uuids,
'image_id': image_id,
'glance_host': FLAGS.glance_host,
'glance_port': FLAGS.glance_port,
'sr_path': get_sr_path(session),
- 'os_type': instance.os_type}
+ 'os_type': os_type}
kwargs = {'params': pickle.dumps(params)}
task = session.async_call_plugin('glance', 'upload_vhd', kwargs)
--
cgit
From 7af17cbef6f3e1c5b052133e40e0edbd8ca9ffb3 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Sun, 6 Mar 2011 10:41:24 -0500
Subject: select cleanups
---
nova/api/openstack/servers.py | 2 +-
nova/quota.py | 14 ++++++--------
nova/virt/xenapi/vmops.py | 2 +-
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 7c620dbc6..93f504f91 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -153,7 +153,7 @@ class Controller(wsgi.Controller):
try:
path = item['path']
contents = item['contents']
- except KeyError, key:
+ except KeyError as key:
expl = 'Bad personality format: missing %s' % key
raise exc.HTTPBadRequest(explanation=expl)
except TypeError:
diff --git a/nova/quota.py b/nova/quota.py
index 4b777624c..1d79f9ef2 100644
--- a/nova/quota.py
+++ b/nova/quota.py
@@ -46,14 +46,12 @@ flags.DEFINE_integer('quota_personality_max_path_bytes', 255,
def get_quota(context, project_id):
- rval = {
- 'instances': FLAGS.quota_instances,
- 'cores': FLAGS.quota_cores,
- 'volumes': FLAGS.quota_volumes,
- 'gigabytes': FLAGS.quota_gigabytes,
- 'floating_ips': FLAGS.quota_floating_ips,
- 'metadata_items': FLAGS.quota_metadata_items,
- }
+ rval = {'instances': FLAGS.quota_instances,
+ 'cores': FLAGS.quota_cores,
+ 'volumes': FLAGS.quota_volumes,
+ 'gigabytes': FLAGS.quota_gigabytes,
+ 'floating_ips': FLAGS.quota_floating_ips,
+ 'metadata_items': FLAGS.quota_metadata_items}
try:
quota = db.quota_get(context, project_id)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 89d58a664..cf4bedaa9 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -316,7 +316,7 @@ class VMOps(object):
def inject_file(self, instance, path, contents):
"""Write a file to the VM instance. The path to which it is to be
- written and the contents of the file need to be supplied; both should
+ written and the contents of the file need to be supplied; both will
be base64-encoded to prevent errors with non-ASCII characters being
transmitted. If the agent does not support file injection, or the user
has disabled it, a NotImplementedError will be raised.
--
cgit
From aa4b8a557505108341d603659a5456d10d5f9632 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Sun, 6 Mar 2011 12:06:39 -0500
Subject: avoid possible string/int comparison problems
---
nova/quota.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/quota.py b/nova/quota.py
index 1d79f9ef2..fdef42bc5 100644
--- a/nova/quota.py
+++ b/nova/quota.py
@@ -115,17 +115,17 @@ def allowed_metadata_items(context, num_metadata_items):
def allowed_personality_files(context):
"""Return the number of personality files allowed"""
- return FLAGS.quota_personality_max_files
+ return int(FLAGS.quota_personality_max_files)
def allowed_personality_content_bytes(context):
"""Return the number of bytes allowed per personality content"""
- return FLAGS.quota_personality_max_content_bytes
+ return int(FLAGS.quota_personality_max_content_bytes)
def allowed_personality_path_bytes(context):
"""Return the number of bytes allowed in a personality file path"""
- return FLAGS.quota_personality_max_path_bytes
+ return int(FLAGS.quota_personality_max_path_bytes)
class QuotaError(exception.ApiError):
--
cgit
From 81fe66bb19d16c387705e144c9941096cc497cf0 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Mon, 7 Mar 2011 15:12:26 +0100
Subject: Make "dhcpbridge init" output correctly formatted leases information.
---
bin/nova-dhcpbridge | 2 +-
nova/network/linux_net.py | 30 +++++++++++++++++++++++++++---
2 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge
index 3dd9de367..7ef51feba 100755
--- a/bin/nova-dhcpbridge
+++ b/bin/nova-dhcpbridge
@@ -94,7 +94,7 @@ def init_leases(interface):
"""Get the list of hosts for an interface."""
ctxt = context.get_admin_context()
network_ref = db.network_get_by_bridge(ctxt, interface)
- return linux_net.get_dhcp_hosts(ctxt, network_ref['id'])
+ return linux_net.get_dhcp_leases(ctxt, network_ref['id'])
def main():
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 535ce87bc..0bcc36081 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -18,6 +18,7 @@ Implements vlans, bridges, and iptables rules using linux utilities.
"""
import os
+import time
from nova import db
from nova import exception
@@ -56,6 +57,8 @@ flags.DEFINE_bool('use_nova_chains', False,
'use the nova_ routing chains instead of default')
flags.DEFINE_string('input_chain', 'INPUT',
'chain to add nova_input to')
+flags.DEFINE_integer('dhcp_lease_time', 120,
+ 'Lifetime of a DHCP lease')
flags.DEFINE_string('dns_server', None,
'if set, uses specific dns server for dnsmasq')
@@ -273,8 +276,17 @@ def ensure_bridge(bridge, interface, net_attrs=None):
_confirm_rule("FORWARD", "-j nova-local")
+def get_dhcp_leases(context, network_id):
+ """Return a network's hosts config in dnsmasq leasefile format"""
+ hosts = []
+ for fixed_ip_ref in db.network_get_associated_fixed_ips(context,
+ network_id):
+ hosts.append(_host_lease(fixed_ip_ref))
+ return '\n'.join(hosts)
+
+
def get_dhcp_hosts(context, network_id):
- """Get a string containing a network's hosts config in dnsmasq format"""
+ """Get a string containing a network's hosts config in dhcp-host format"""
hosts = []
for fixed_ip_ref in db.network_get_associated_fixed_ips(context,
network_id):
@@ -365,8 +377,19 @@ interface %s
utils.get_my_linklocal(network_ref['bridge'])})
+def _host_lease(fixed_ip_ref):
+ """Return a host string for an address in leasefile format"""
+ instance_ref = fixed_ip_ref['instance']
+ timestamp = time.mktime(instance_ref['updated_at'].timetuple())
+
+ return "%d %s %s %s" % (timestamp + FLAGS.dhcp_lease_time,
+ instance_ref['mac_address'],
+ instance_ref['hostname'],
+ fixed_ip_ref['address'])
+
+
def _host_dhcp(fixed_ip_ref):
- """Return a host string for an address"""
+ """Return a host string for an address in dhcp-host format"""
instance_ref = fixed_ip_ref['instance']
return "%s,%s.%s,%s" % (instance_ref['mac_address'],
instance_ref['hostname'],
@@ -420,7 +443,8 @@ def _dnsmasq_cmd(net):
' --pid-file=%s' % _dhcp_file(net['bridge'], 'pid'),
' --listen-address=%s' % net['gateway'],
' --except-interface=lo',
- ' --dhcp-range=%s,static,120s' % net['dhcp_start'],
+ ' --dhcp-range=%s,static,%ds' % (net['dhcp_start'],
+ FLAGS.dhcp_lease_time),
' --dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'),
' --dhcp-script=%s' % FLAGS.dhcpbridge,
' --leasefile-ro']
--
cgit
From b8a0fdca4df454a4d60df40d06ebd82bcc2ba3da Mon Sep 17 00:00:00 2001
From: Cory Wright
Date: Tue, 8 Mar 2011 14:35:53 +0000
Subject: * pep8 cleanups in migrations * a few bugfixes
---
.../sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py | 5 +----
nova/tests/test_xenapi.py | 4 ++--
nova/virt/xenapi/vm_utils.py | 2 +-
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
index a50f31e16..514b92b81 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
@@ -29,8 +29,6 @@ instances = Table('instances', meta,
Column('id', Integer(), primary_key=True, nullable=False),
)
-# FIXME(dubs) should this be not null? Maybe create as nullable, then
-# populate all existing rows with 'linux', then adding not null constraint.
instances_os_type = Column('os_type',
String(length=255, convert_unicode=False,
assert_unicode=None, unicode_error=None,
@@ -45,7 +43,7 @@ def upgrade(migrate_engine):
instances.create_column(instances_os_type)
migrate_engine.execute(instances.update()\
- .where(instances.c.os_type==None)\
+ .where(instances.c.os_type == None)\
.values(os_type='linux'))
@@ -53,4 +51,3 @@ def downgrade(migrate_engine):
meta.bind = migrate_engine
instances.drop_column('os_type')
-
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 27f0e5dd7..25070e108 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -251,7 +251,7 @@ class XenAPIVMTestCase(test.TestCase):
self.vm_info = vm_info
self.vm = vm
- def check_vm_record(self):
+ def check_vm_record(self, conn):
# Check that m1.large above turned into the right thing.
instance_type = db.instance_type_get_by_name(conn, 'm1.large')
mem_kib = long(instance_type['memory_mb']) << 10
@@ -321,7 +321,7 @@ class XenAPIVMTestCase(test.TestCase):
instance = db.instance_create(values)
conn.spawn(instance)
self.create_vm_record(conn, os_type)
- self.check_vm_record()
+ self.check_vm_record(conn)
def test_spawn_not_enough_memory(self):
FLAGS.xenapi_image_service = 'glance'
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 150824400..604e8a4e0 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -324,7 +324,7 @@ class VMHelper(HelperBase):
kwargs = {'params': pickle.dumps(params)}
task = session.async_call_plugin('glance', 'upload_vhd', kwargs)
- session.wait_for_task(task, instance_id)
+ session.wait_for_task(task, instance.id)
@classmethod
def fetch_image(cls, session, instance_id, image, user, project,
--
cgit
From cbc2956a4e863c1bc952c7cef6045c39d293818d Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Tue, 8 Mar 2011 17:18:13 +0000
Subject: Remove addition of account to service url.
---
nova/api/openstack/__init__.py | 24 +++-------------
nova/api/openstack/auth.py | 46 ++++++-----------------------
nova/auth/novarc.template | 2 +-
nova/tests/api/openstack/test_accounts.py | 8 +++---
nova/tests/api/openstack/test_adminapi.py | 4 +--
nova/tests/api/openstack/test_auth.py | 2 +-
nova/tests/api/openstack/test_flavors.py | 4 +--
nova/tests/api/openstack/test_images.py | 4 +--
nova/tests/api/openstack/test_servers.py | 48 +++++++++++++++----------------
nova/tests/api/openstack/test_users.py | 10 +++----
nova/tests/api/openstack/test_zones.py | 10 +++----
11 files changed, 59 insertions(+), 103 deletions(-)
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 005d330a6..a655b1c85 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -73,18 +73,6 @@ class APIRouter(wsgi.Router):
def __init__(self):
mapper = routes.Mapper()
- accounts_controller = accounts.Controller()
- mapper.connect("account", "/{id}",
- controller=accounts_controller, action="show",
- conditions=dict(method=["GET"]))
- if FLAGS.allow_admin_api:
- mapper.connect("/{id}",
- controller=accounts_controller, action="update",
- conditions=dict(method=["PUT"]))
- mapper.connect("/{id}",
- controller=accounts_controller, action="delete",
- conditions=dict(method=["DELETE"]))
-
server_members = {'action': 'POST'}
if FLAGS.allow_admin_api:
LOG.debug(_("Including admin operations in API."))
@@ -101,38 +89,34 @@ class APIRouter(wsgi.Router):
server_members['inject_network_info'] = 'POST'
mapper.resource("zone", "zones", controller=zones.Controller(),
- path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("user", "users", controller=users.Controller(),
- path_prefix="{account_id}/",
collection={'detail': 'GET'})
+ mapper.resource("account", "accounts",
+ controller=accounts.Controller(),
+ collection={'detail': 'GET'})
+
mapper.resource("server", "servers", controller=servers.Controller(),
collection={'detail': 'GET'},
- path_prefix="{account_id}/",
member=server_members)
mapper.resource("backup_schedule", "backup_schedule",
controller=backup_schedules.Controller(),
- path_prefix="{account_id}/servers/{server_id}/",
parent_resource=dict(member_name='server',
collection_name='servers'))
mapper.resource("console", "consoles",
controller=consoles.Controller(),
- path_prefix="{account_id}/servers/{server_id}/",
parent_resource=dict(member_name='server',
collection_name='servers'))
mapper.resource("image", "images", controller=images.Controller(),
- path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("flavor", "flavors", controller=flavors.Controller(),
- path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("shared_ip_group", "shared_ip_groups",
- path_prefix="{account_id}/",
collection={'detail': 'GET'},
controller=shared_ip_groups.Controller())
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index e77910fed..e71fc69e3 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -53,19 +53,15 @@ class AuthMiddleware(wsgi.Middleware):
if not self.has_authentication(req):
return self.authenticate(req)
user = self.get_user_by_authentication(req)
- account_name = req.path_info_peek()
-
+ accounts = self.auth.get_projects(user=user)
if not user:
return faults.Fault(webob.exc.HTTPUnauthorized())
- if not account_name:
- if self.auth.is_admin(user):
- account_name = FLAGS.default_project
- else:
- return faults.Fault(webob.exc.HTTPUnauthorized())
- try:
- account = self.auth.get_project(account_name)
- except exception.NotFound:
+ if accounts:
+ #we are punting on this til auth is settled,
+ #and possibly til api v1.1 (mdragon)
+ account = accounts[0]
+ else:
return faults.Fault(webob.exc.HTTPUnauthorized())
if not self.auth.is_admin(user) and \
@@ -85,7 +81,6 @@ class AuthMiddleware(wsgi.Middleware):
# Unless the request is explicitly made against // don't
# honor it
path_info = req.path_info
- account_name = None
if len(path_info) > 1:
return faults.Fault(webob.exc.HTTPUnauthorized())
@@ -95,10 +90,7 @@ class AuthMiddleware(wsgi.Middleware):
except KeyError:
return faults.Fault(webob.exc.HTTPUnauthorized())
- if ':' in username:
- account_name, username = username.rsplit(':', 1)
-
- token, user = self._authorize_user(username, account_name, key, req)
+ token, user = self._authorize_user(username, key, req)
if user and token:
res = webob.Response()
res.headers['X-Auth-Token'] = token.token_hash
@@ -135,31 +127,15 @@ class AuthMiddleware(wsgi.Middleware):
return self.auth.get_user(token.user_id)
return None
- def _authorize_user(self, username, account_name, key, req):
+ def _authorize_user(self, username, key, req):
"""Generates a new token and assigns it to a user.
username - string
- account_name - string
key - string API key
req - webob.Request object
"""
ctxt = context.get_admin_context()
user = self.auth.get_user_from_access_key(key)
- if account_name:
- try:
- account = self.auth.get_project(account_name)
- except exception.NotFound:
- return None, None
- else:
- # (dragondm) punt and try to determine account.
- # this is something of a hack, but a user on 1 account is a
- # common case, and is the way the current RS code works.
- accounts = self.auth.get_projects(user=user)
- if len(accounts) == 1:
- account = accounts[0]
- else:
- #we can't tell what account they are logging in for.
- return None, None
if user and user.name == username:
token_hash = hashlib.sha1('%s%s%f' % (username, key,
@@ -167,11 +143,7 @@ class AuthMiddleware(wsgi.Middleware):
token_dict = {}
token_dict['token_hash'] = token_hash
token_dict['cdn_management_url'] = ''
- # auth url + project (account) id, e.g.
- # http://foo.org:8774/baz/v1.0/myacct/
- os_url = '%s%s%s/' % (req.url,
- '' if req.url.endswith('/') else '/',
- account.id)
+ os_url = req.url
token_dict['server_management_url'] = os_url
token_dict['storage_url'] = ''
token_dict['user_id'] = user.id
diff --git a/nova/auth/novarc.template b/nova/auth/novarc.template
index 1c917ad44..cda2ecc28 100644
--- a/nova/auth/novarc.template
+++ b/nova/auth/novarc.template
@@ -11,5 +11,5 @@ export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this se
alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}"
alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}"
export NOVA_API_KEY="%(access)s"
-export NOVA_USERNAME="%(project)s:%(user)s"
+export NOVA_USERNAME="%(user)s"
export NOVA_URL="%(os)s"
diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py
index b2e89824a..746f02f57 100644
--- a/nova/tests/api/openstack/test_accounts.py
+++ b/nova/tests/api/openstack/test_accounts.py
@@ -70,7 +70,7 @@ class AccountsTest(test.TestCase):
super(AccountsTest, self).tearDown()
def test_get_account(self):
- req = webob.Request.blank('/v1.0/test1')
+ req = webob.Request.blank('/v1.0/accounts/test1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -80,7 +80,7 @@ class AccountsTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_account_delete(self):
- req = webob.Request.blank('/v1.0/test1')
+ req = webob.Request.blank('/v1.0/accounts/test1')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertTrue('test1' not in fakes.FakeAuthManager.projects)
@@ -89,7 +89,7 @@ class AccountsTest(test.TestCase):
def test_account_create(self):
body = dict(account=dict(description='test account',
manager='guy1'))
- req = webob.Request.blank('/v1.0/newacct')
+ req = webob.Request.blank('/v1.0/accounts/newacct')
req.method = 'PUT'
req.body = json.dumps(body)
@@ -108,7 +108,7 @@ class AccountsTest(test.TestCase):
def test_account_update(self):
body = dict(account=dict(description='test account',
manager='guy2'))
- req = webob.Request.blank('/v1.0/test1')
+ req = webob.Request.blank('/v1.0/accounts/test1')
req.method = 'PUT'
req.body = json.dumps(body)
diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py
index 7cb9e8450..4568cb9f5 100644
--- a/nova/tests/api/openstack/test_adminapi.py
+++ b/nova/tests/api/openstack/test_adminapi.py
@@ -50,7 +50,7 @@ class AdminAPITest(test.TestCase):
def test_admin_enabled(self):
FLAGS.allow_admin_api = True
# We should still be able to access public operations.
- req = webob.Request.blank('/v1.0/testacct/flavors')
+ req = webob.Request.blank('/v1.0/flavors')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
# TODO: Confirm admin operations are available.
@@ -58,7 +58,7 @@ class AdminAPITest(test.TestCase):
def test_admin_disabled(self):
FLAGS.allow_admin_api = False
# We should still be able to access public operations.
- req = webob.Request.blank('/v1.0/testacct/flavors')
+ req = webob.Request.blank('/v1.0/flavors')
res = req.get_response(fakes.wsgi_app())
# TODO: Confirm admin operations are unavailable.
self.assertEqual(res.status_int, 200)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index 8268a6fb9..49f90879d 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -78,7 +78,7 @@ class Test(test.TestCase):
self.assertEqual(result.status, '204 No Content')
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
self.assertEqual(result.headers['X-Server-Management-Url'],
- "http://foo/v1.0/test/")
+ "http://foo/v1.0/")
self.assertEqual(result.headers['X-CDN-Management-Url'],
"")
self.assertEqual(result.headers['X-Storage-Url'], "")
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index ba0785b0e..8280a505f 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -42,11 +42,11 @@ class FlavorsTest(test.TestCase):
super(FlavorsTest, self).tearDown()
def test_get_flavor_list(self):
- req = webob.Request.blank('/v1.0/testacct/flavors')
+ req = webob.Request.blank('/v1.0/flavors')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
def test_get_flavor_by_id(self):
- req = webob.Request.blank('/v1.0/testacct/flavors/1')
+ req = webob.Request.blank('/v1.0/flavors/1')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 819ca001e..dbe507f7d 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -216,7 +216,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
super(ImageControllerWithGlanceServiceTest, self).tearDown()
def test_get_image_index(self):
- req = webob.Request.blank('/v1.0/testacct/images')
+ req = webob.Request.blank('/v1.0/images')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -228,7 +228,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
"image %s not in fixture index!" % str(image))
def test_get_image_details(self):
- req = webob.Request.blank('/v1.0/testacct/images/detail')
+ req = webob.Request.blank('/v1.0/images/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index d592e06b0..705a2f800 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -150,7 +150,7 @@ class ServersTest(test.TestCase):
super(ServersTest, self).tearDown()
def test_get_server_by_id(self):
- req = webob.Request.blank('/v1.0/testacct/servers/1')
+ req = webob.Request.blank('/v1.0/servers/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['server']['id'], '1')
@@ -161,7 +161,7 @@ class ServersTest(test.TestCase):
public = ["1.2.3.4"]
new_return_server = return_server_with_addresses(private, public)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
- req = webob.Request.blank('/v1.0/testacct/servers/1')
+ req = webob.Request.blank('/v1.0/servers/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['server']['id'], '1')
@@ -173,7 +173,7 @@ class ServersTest(test.TestCase):
self.assertEqual(addresses["private"][0], private)
def test_get_server_list(self):
- req = webob.Request.blank('/v1.0/testacct/servers')
+ req = webob.Request.blank('/v1.0/servers')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -224,7 +224,7 @@ class ServersTest(test.TestCase):
name='server_test', imageId=2, flavorId=2,
metadata={'hello': 'world', 'open': 'stack'},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers')
+ req = webob.Request.blank('/v1.0/servers')
req.method = 'POST'
req.body = json.dumps(body)
@@ -233,7 +233,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_update_no_body(self):
- req = webob.Request.blank('/v1.0/testacct/servers/1')
+ req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 422)
@@ -251,7 +251,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
- req = webob.Request.blank('/v1.0/testacct/servers/1')
+ req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
req.body = self.body
req.get_response(fakes.wsgi_app())
@@ -267,30 +267,30 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
- req = webob.Request.blank('/v1.0/testacct/servers/1')
+ req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
req.body = self.body
req.get_response(fakes.wsgi_app())
def test_create_backup_schedules(self):
- req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
+ req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
req.method = 'POST'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_delete_backup_schedules(self):
- req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
+ req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_get_server_backup_schedules(self):
- req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
+ req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_get_all_server_details(self):
- req = webob.Request.blank('/v1.0/testacct/servers/detail')
+ req = webob.Request.blank('/v1.0/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -321,7 +321,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
return_servers_with_host)
- req = webob.Request.blank('/v1.0/testacct/servers/detail')
+ req = webob.Request.blank('/v1.0/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -341,7 +341,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers/1/pause')
+ req = webob.Request.blank('/v1.0/servers/1/pause')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -353,7 +353,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers/1/unpause')
+ req = webob.Request.blank('/v1.0/servers/1/unpause')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -365,7 +365,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers/1/suspend')
+ req = webob.Request.blank('/v1.0/servers/1/suspend')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -377,7 +377,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers/1/resume')
+ req = webob.Request.blank('/v1.0/servers/1/resume')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -389,7 +389,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers/1/reset_network')
+ req = webob.Request.blank('/v1.0/servers/1/reset_network')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -402,7 +402,7 @@ class ServersTest(test.TestCase):
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank(
- '/v1.0/testacct/servers/1/inject_network_info')
+ '/v1.0/servers/1/inject_network_info')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -410,13 +410,13 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_server_diagnostics(self):
- req = webob.Request.blank("/v1.0/testacct/servers/1/diagnostics")
+ req = webob.Request.blank("/v1.0/servers/1/diagnostics")
req.method = "GET"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
def test_server_actions(self):
- req = webob.Request.blank("/v1.0/testacct/servers/1/actions")
+ req = webob.Request.blank("/v1.0/servers/1/actions")
req.method = "GET"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
@@ -425,7 +425,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers/1/action')
+ req = webob.Request.blank('/v1.0/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -435,7 +435,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers/1/action')
+ req = webob.Request.blank('/v1.0/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -445,14 +445,14 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
- req = webob.Request.blank('/v1.0/testacct/servers/1/action')
+ req = webob.Request.blank('/v1.0/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
def test_delete_server_instance(self):
- req = webob.Request.blank('/v1.0/testacct/servers/1')
+ req = webob.Request.blank('/v1.0/servers/1')
req.method = 'DELETE'
self.server_delete_called = False
diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py
index bd32254cd..14c7897f0 100644
--- a/nova/tests/api/openstack/test_users.py
+++ b/nova/tests/api/openstack/test_users.py
@@ -72,7 +72,7 @@ class UsersTest(test.TestCase):
super(UsersTest, self).tearDown()
def test_get_user_list(self):
- req = webob.Request.blank('/v1.0/testacct/users')
+ req = webob.Request.blank('/v1.0/users')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -80,7 +80,7 @@ class UsersTest(test.TestCase):
self.assertEqual(len(res_dict['users']), 2)
def test_get_user_by_id(self):
- req = webob.Request.blank('/v1.0/testacct/users/guy2')
+ req = webob.Request.blank('/v1.0/users/guy2')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -91,7 +91,7 @@ class UsersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_user_delete(self):
- req = webob.Request.blank('/v1.0/testacct/users/guy1')
+ req = webob.Request.blank('/v1.0/users/guy1')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertTrue('guy1' not in [u.id for u in
@@ -103,7 +103,7 @@ class UsersTest(test.TestCase):
access='acc3',
secret='invasionIsInNormandy',
admin=True))
- req = webob.Request.blank('/v1.0/testacct/users')
+ req = webob.Request.blank('/v1.0/users')
req.method = 'POST'
req.body = json.dumps(body)
@@ -124,7 +124,7 @@ class UsersTest(test.TestCase):
body = dict(user=dict(name='guy2',
access='acc2',
secret='invasionIsInNormandy'))
- req = webob.Request.blank('/v1.0/testacct/users/guy2')
+ req = webob.Request.blank('/v1.0/users/guy2')
req.method = 'PUT'
req.body = json.dumps(body)
diff --git a/nova/tests/api/openstack/test_zones.py b/nova/tests/api/openstack/test_zones.py
index 51f13af48..6d869dc15 100644
--- a/nova/tests/api/openstack/test_zones.py
+++ b/nova/tests/api/openstack/test_zones.py
@@ -85,7 +85,7 @@ class ZonesTest(test.TestCase):
super(ZonesTest, self).tearDown()
def test_get_zone_list(self):
- req = webob.Request.blank('/v1.0/testacct/zones')
+ req = webob.Request.blank('/v1.0/zones')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -93,7 +93,7 @@ class ZonesTest(test.TestCase):
self.assertEqual(len(res_dict['zones']), 2)
def test_get_zone_by_id(self):
- req = webob.Request.blank('/v1.0/testacct/zones/1')
+ req = webob.Request.blank('/v1.0/zones/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -103,7 +103,7 @@ class ZonesTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_zone_delete(self):
- req = webob.Request.blank('/v1.0/testacct/zones/1')
+ req = webob.Request.blank('/v1.0/zones/1')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
@@ -111,7 +111,7 @@ class ZonesTest(test.TestCase):
def test_zone_create(self):
body = dict(zone=dict(api_url='http://blah.zoo', username='fred',
password='fubar'))
- req = webob.Request.blank('/v1.0/testacct/zones')
+ req = webob.Request.blank('/v1.0/zones')
req.method = 'POST'
req.body = json.dumps(body)
@@ -125,7 +125,7 @@ class ZonesTest(test.TestCase):
def test_zone_update(self):
body = dict(zone=dict(username='zeb', password='sneaky'))
- req = webob.Request.blank('/v1.0/testacct/zones/1')
+ req = webob.Request.blank('/v1.0/zones/1')
req.method = 'PUT'
req.body = json.dumps(body)
--
cgit
From a74bf3381ada34f35c43d6f307fbae9abecfb255 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Tue, 8 Mar 2011 10:30:48 -0800
Subject: abstracted network code in the base class for flat and vlan
---
nova/network/manager.py | 8 +-
nova/tests/network/__init__.py | 47 +++++++++
nova/tests/network/base.py | 154 ++++++++++++++++++++++++++++++
nova/tests/test_flat_network.py | 147 ++++------------------------
nova/tests/test_vlan_network.py | 205 ++++++++--------------------------------
5 files changed, 259 insertions(+), 302 deletions(-)
create mode 100644 nova/tests/network/__init__.py
create mode 100644 nova/tests/network/base.py
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 686ab9732..670ff55fd 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -404,9 +404,11 @@ class FlatManager(NetworkManager):
net = {}
net['injected'] = FLAGS.flat_injected
net['dns'] = FLAGS.flat_network_dns
- if(FLAGS.use_ipv6):
- net['gateway_v6'] = \
- utils.get_my_linklocal(FLAGS.flat_network_bridge)
+ if not FLAGS.fake_network:
+ if(FLAGS.use_ipv6):
+ net['gateway_v6'] = \
+ utils.get_my_linklocal(
+ FLAGS.flat_network_bridge)
self.db.network_update(context, network_id, net)
def allocate_floating_ip(self, context, project_id):
diff --git a/nova/tests/network/__init__.py b/nova/tests/network/__init__.py
new file mode 100644
index 000000000..e51065983
--- /dev/null
+++ b/nova/tests/network/__init__.py
@@ -0,0 +1,47 @@
+import os
+
+from nova import context
+from nova import db
+from nova import flags
+from nova import log as logging
+from nova import utils
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.network')
+
+
+def binpath(script):
+ """Returns the absolute path to a script in bin"""
+ return os.path.abspath(os.path.join(__file__, "../../../../bin", script))
+
+
+def lease_ip(private_ip):
+ """Run add command on dhcpbridge"""
+ network_ref = db.fixed_ip_get_network(context.get_admin_context(),
+ private_ip)
+ instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
+ private_ip)
+ cmd = "%s add %s %s fake" % (binpath('nova-dhcpbridge'),
+ instance_ref['mac_address'],
+ private_ip)
+ env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
+ 'TESTING': '1',
+ 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
+ (out, err) = utils.execute(cmd, addl_env=env)
+ LOG.debug("ISSUE_IP: %s, %s ", out, err)
+
+
+def release_ip(private_ip):
+ """Run del command on dhcpbridge"""
+ network_ref = db.fixed_ip_get_network(context.get_admin_context(),
+ private_ip)
+ instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
+ private_ip)
+ cmd = "%s del %s %s fake" % (binpath('nova-dhcpbridge'),
+ instance_ref['mac_address'],
+ private_ip)
+ env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
+ 'TESTING': '1',
+ 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
+ (out, err) = utils.execute(cmd, addl_env=env)
+ LOG.debug("RELEASE_IP: %s, %s ", out, err)
diff --git a/nova/tests/network/base.py b/nova/tests/network/base.py
new file mode 100644
index 000000000..2dd8178ff
--- /dev/null
+++ b/nova/tests/network/base.py
@@ -0,0 +1,154 @@
+# 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.
+"""
+Base class of Unit Tests for all network models
+"""
+import IPy
+import os
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.auth import manager
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.network')
+
+
+class NetworkTestCase(test.TestCase):
+ """Test cases for network code"""
+ def setUp(self):
+ super(NetworkTestCase, self).setUp()
+ # NOTE(vish): if you change these flags, make sure to change the
+ # flags in the corresponding section in nova-dhcpbridge
+ self.flags(connection_type='fake',
+ fake_call=True,
+ fake_network=True)
+ self.manager = manager.AuthManager()
+ self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
+ self.projects = []
+ self.network = utils.import_object(FLAGS.network_manager)
+ self.context = context.RequestContext(project=None, user=self.user)
+ for i in range(FLAGS.num_networks):
+ name = 'project%s' % i
+ project = self.manager.create_project(name, 'netuser', name)
+ self.projects.append(project)
+ # create the necessary network data for the project
+ user_context = context.RequestContext(project=self.projects[i],
+ user=self.user)
+ host = self.network.get_network_host(user_context.elevated())
+ instance_ref = self._create_instance(0)
+ self.instance_id = instance_ref['id']
+ instance_ref = self._create_instance(1)
+ self.instance2_id = instance_ref['id']
+
+ def tearDown(self):
+ # TODO(termie): this should really be instantiating clean datastores
+ # in between runs, one failure kills all the tests
+ db.instance_destroy(context.get_admin_context(), self.instance_id)
+ db.instance_destroy(context.get_admin_context(), self.instance2_id)
+ for project in self.projects:
+ self.manager.delete_project(project)
+ self.manager.delete_user(self.user)
+ super(NetworkTestCase, self).tearDown()
+
+ def _create_instance(self, project_num, mac=None):
+ if not mac:
+ mac = utils.generate_mac()
+ project = self.projects[project_num]
+ self.context._project = project
+ self.context.project_id = project.id
+ return db.instance_create(self.context,
+ {'project_id': project.id,
+ 'mac_address': mac})
+
+ def _create_address(self, project_num, instance_id=None):
+ """Create an address in given project num"""
+ if instance_id is None:
+ instance_id = self.instance_id
+ self.context._project = self.projects[project_num]
+ self.context.project_id = self.projects[project_num].id
+ return self.network.allocate_fixed_ip(self.context, instance_id)
+
+ def _deallocate_address(self, project_num, address):
+ self.context._project = self.projects[project_num]
+ self.context.project_id = self.projects[project_num].id
+ self.network.deallocate_fixed_ip(self.context, address)
+
+ def _is_allocated_in_project(self, address, project_id):
+ """Returns true if address is in specified project"""
+ project_net = db.network_get_by_bridge(context.get_admin_context(),
+ FLAGS.flat_network_bridge)
+ network = db.fixed_ip_get_network(context.get_admin_context(),
+ address)
+ instance = db.fixed_ip_get_instance(context.get_admin_context(),
+ address)
+ # instance exists until release
+ return instance is not None and network['id'] == project_net['id']
+
+ def test_private_ipv6(self):
+ """Make sure ipv6 is OK"""
+ if FLAGS.use_ipv6:
+ instance_ref = self._create_instance(0)
+ address = self._create_address(0, instance_ref['id'])
+ network_ref = db.project_get_network(
+ context.get_admin_context(),
+ self.context.project_id)
+ address_v6 = db.instance_get_fixed_address_v6(
+ context.get_admin_context(),
+ instance_ref['id'])
+ self.assertEqual(instance_ref['mac_address'],
+ utils.to_mac(address_v6))
+ instance_ref2 = db.fixed_ip_get_instance_v6(
+ context.get_admin_context(),
+ address_v6)
+ self.assertEqual(instance_ref['id'], instance_ref2['id'])
+ self.assertEqual(address_v6,
+ utils.to_global_ipv6(
+ network_ref['cidr_v6'],
+ instance_ref['mac_address']))
+ self._deallocate_address(0, address)
+ db.instance_destroy(context.get_admin_context(),
+ instance_ref['id'])
+
+ def test_available_ips(self):
+ """Make sure the number of available ips for the network is correct
+
+ The number of available IP addresses depends on the test
+ environment's setup.
+
+ Network size is set in test fixture's setUp method.
+
+ There are ips reserved at the bottom and top of the range.
+ services (network, gateway, CloudPipe, broadcast)
+ """
+ network = db.project_get_network(context.get_admin_context(),
+ self.projects[0].id)
+ net_size = flags.FLAGS.network_size
+ admin_context = context.get_admin_context()
+ total_ips = (db.network_count_available_ips(admin_context,
+ network['id']) +
+ db.network_count_reserved_ips(admin_context,
+ network['id']) +
+ db.network_count_allocated_ips(admin_context,
+ network['id']))
+ self.assertEqual(total_ips, net_size)
diff --git a/nova/tests/test_flat_network.py b/nova/tests/test_flat_network.py
index 91a49920d..a9a934158 100644
--- a/nova/tests/test_flat_network.py
+++ b/nova/tests/test_flat_network.py
@@ -30,96 +30,15 @@ from nova import log as logging
from nova import test
from nova import utils
from nova.auth import manager
+from nova.tests.network import base
+
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.tests.network')
-class FlatNetworkTestCase(test.TestCase):
+class FlatNetworkTestCase(base.NetworkTestCase):
"""Test cases for network code"""
- def setUp(self):
- super(FlatNetworkTestCase, self).setUp()
- # NOTE(vish): if you change these flags, make sure to change the
- # flags in the corresponding section in nova-dhcpbridge
- self.flags(connection_type='fake',
- fake_call=True,
- fake_network=True)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
- self.projects = []
- self.network = utils.import_object(FLAGS.network_manager)
- self.context = context.RequestContext(project=None, user=self.user)
- for i in range(5):
- name = 'project%s' % i
- project = self.manager.create_project(name, 'netuser', name)
- self.projects.append(project)
- # create the necessary network data for the project
- user_context = context.RequestContext(project=self.projects[i],
- user=self.user)
- host = self.network.get_network_host(user_context.elevated())
- instance_ref = self._create_instance(0)
- self.instance_id = instance_ref['id']
- instance_ref = self._create_instance(1)
- self.instance2_id = instance_ref['id']
-
- def tearDown(self):
- # TODO(termie): this should really be instantiating clean datastores
- # in between runs, one failure kills all the tests
- db.instance_destroy(context.get_admin_context(), self.instance_id)
- db.instance_destroy(context.get_admin_context(), self.instance2_id)
- for project in self.projects:
- self.manager.delete_project(project)
- self.manager.delete_user(self.user)
- super(FlatNetworkTestCase, self).tearDown()
-
- def _create_instance(self, project_num, mac=None):
- if not mac:
- mac = utils.generate_mac()
- project = self.projects[project_num]
- self.context._project = project
- self.context.project_id = project.id
- return db.instance_create(self.context,
- {'project_id': project.id,
- 'mac_address': mac})
-
- def _create_address(self, project_num, instance_id=None):
- """Create an address in given project num"""
- if instance_id is None:
- instance_id = self.instance_id
- self.context._project = self.projects[project_num]
- self.context.project_id = self.projects[project_num].id
- return self.network.allocate_fixed_ip(self.context, instance_id)
-
- def _deallocate_address(self, project_num, address):
- self.context._project = self.projects[project_num]
- self.context.project_id = self.projects[project_num].id
- self.network.deallocate_fixed_ip(self.context, address)
-
- def test_private_ipv6(self):
- """Make sure ipv6 is OK"""
- if FLAGS.use_ipv6:
- instance_ref = self._create_instance(0)
- address = self._create_address(0, instance_ref['id'])
- network_ref = db.project_get_network(
- context.get_admin_context(),
- self.context.project_id)
- address_v6 = db.instance_get_fixed_address_v6(
- context.get_admin_context(),
- instance_ref['id'])
- self.assertEqual(instance_ref['mac_address'],
- utils.to_mac(address_v6))
- instance_ref2 = db.fixed_ip_get_instance_v6(
- context.get_admin_context(),
- address_v6)
- self.assertEqual(instance_ref['id'], instance_ref2['id'])
- self.assertEqual(address_v6,
- utils.to_global_ipv6(
- network_ref['cidr_v6'],
- instance_ref['mac_address']))
- self._deallocate_address(0, address)
- db.instance_destroy(context.get_admin_context(),
- instance_ref['id'])
-
def test_public_network_association(self):
"""Makes sure that we can allocate a public ip"""
# TODO(vish): better way of adding floating ips
@@ -167,28 +86,34 @@ class FlatNetworkTestCase(test.TestCase):
def test_allocate_deallocate_fixed_ip(self):
"""Makes sure that we can allocate and deallocate a fixed ip"""
address = self._create_address(0)
- self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
+ self.assertTrue(self._is_allocated_in_project(address,
+ self.projects[0].id))
self._deallocate_address(0, address)
# check if the fixed ip address is really deallocated
- self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
+ self.assertFalse(self._is_allocated_in_project(address,
+ self.projects[0].id))
def test_side_effects(self):
"""Ensures allocating and releasing has no side effects"""
address = self._create_address(0)
address2 = self._create_address(1, self.instance2_id)
- self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
- self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
+ self.assertTrue(self._is_allocated_in_project(address,
+ self.projects[0].id))
+ self.assertTrue(self._is_allocated_in_project(address2,
+ self.projects[1].id))
self._deallocate_address(0, address)
- self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
+ self.assertFalse(self._is_allocated_in_project(address,
+ self.projects[0].id))
# First address release shouldn't affect the second
- self.assertTrue(is_allocated_in_project(address2, self.projects[0].id))
+ self.assertTrue(self._is_allocated_in_project(address2,
+ self.projects[0].id))
self._deallocate_address(1, address2)
- self.assertFalse(is_allocated_in_project(address2,
+ self.assertFalse(self._is_allocated_in_project(address2,
self.projects[1].id))
def test_ips_are_reused(self):
@@ -201,29 +126,6 @@ class FlatNetworkTestCase(test.TestCase):
self.network.deallocate_fixed_ip(self.context, address2)
- def test_available_ips(self):
- """Make sure the number of available ips for the network is correct
-
- The number of available IP addresses depends on the test
- environment's setup.
-
- Network size is set in test fixture's setUp method.
-
- There are ips reserved at the bottom and top of the range.
- services (network, gateway, CloudPipe, broadcast)
- """
- network = db.project_get_network(context.get_admin_context(),
- self.projects[0].id)
- net_size = flags.FLAGS.network_size
- admin_context = context.get_admin_context()
- total_ips = (db.network_count_available_ips(admin_context,
- network['id']) +
- db.network_count_reserved_ips(admin_context,
- network['id']) +
- db.network_count_allocated_ips(admin_context,
- network['id']))
- self.assertEqual(total_ips, net_size)
-
def test_too_many_addresses(self):
"""Test for a NoMoreAddresses exception when all fixed ips are used.
"""
@@ -257,20 +159,3 @@ class FlatNetworkTestCase(test.TestCase):
def run(self, result=None):
if(FLAGS.network_manager == 'nova.network.manager.FlatManager'):
super(FlatNetworkTestCase, self).run(result)
-
-
-def is_allocated_in_project(address, project_id):
- """Returns true if address is in specified project"""
- #project_net = db.project_get_network(context.get_admin_context(),
- # project_id)
- project_net = db.network_get_by_bridge(context.get_admin_context(),
- FLAGS.flat_network_bridge)
- network = db.fixed_ip_get_network(context.get_admin_context(), address)
- instance = db.fixed_ip_get_instance(context.get_admin_context(), address)
- # instance exists until release
- return instance is not None and network['id'] == project_net['id']
-
-
-def binpath(script):
- """Returns the absolute path to a script in bin"""
- return os.path.abspath(os.path.join(__file__, "../../../bin", script))
diff --git a/nova/tests/test_vlan_network.py b/nova/tests/test_vlan_network.py
index be39313bb..f3478320b 100644
--- a/nova/tests/test_vlan_network.py
+++ b/nova/tests/test_vlan_network.py
@@ -29,100 +29,16 @@ from nova import log as logging
from nova import test
from nova import utils
from nova.auth import manager
+from nova.tests.network import base
+from nova.tests.network import binpath,\
+ lease_ip, release_ip
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.tests.network')
-class VlanNetworkTestCase(test.TestCase):
+class VlanNetworkTestCase(base.NetworkTestCase):
"""Test cases for network code"""
- def setUp(self):
- super(VlanNetworkTestCase, self).setUp()
- # NOTE(vish): if you change these flags, make sure to change the
- # flags in the corresponding section in nova-dhcpbridge
- self.flags(connection_type='fake',
- fake_call=True,
- fake_network=True)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
- self.projects = []
- self.network = utils.import_object(FLAGS.network_manager)
- self.context = context.RequestContext(project=None, user=self.user)
- for i in range(FLAGS.num_networks):
- name = 'project%s' % i
- project = self.manager.create_project(name, 'netuser', name)
- self.projects.append(project)
- # create the necessary network data for the project
- user_context = context.RequestContext(project=self.projects[i],
- user=self.user)
- host = self.network.get_network_host(user_context.elevated())
- instance_ref = self._create_instance(0)
- self.instance_id = instance_ref['id']
- instance_ref = self._create_instance(1)
- self.instance2_id = instance_ref['id']
-
- def tearDown(self):
- # TODO(termie): this should really be instantiating clean datastores
- # in between runs, one failure kills all the tests
- db.instance_destroy(context.get_admin_context(), self.instance_id)
- db.instance_destroy(context.get_admin_context(), self.instance2_id)
- for project in self.projects:
- self.manager.delete_project(project)
- self.manager.delete_user(self.user)
- super(VlanNetworkTestCase, self).tearDown()
-
- def run(self, result=None):
- if(FLAGS.network_manager == 'nova.network.manager.VlanManager'):
- super(VlanNetworkTestCase, self).run(result)
-
- def _create_instance(self, project_num, mac=None):
- if not mac:
- mac = utils.generate_mac()
- project = self.projects[project_num]
- self.context._project = project
- self.context.project_id = project.id
- return db.instance_create(self.context,
- {'project_id': project.id,
- 'mac_address': mac})
-
- def _create_address(self, project_num, instance_id=None):
- """Create an address in given project num"""
- if instance_id is None:
- instance_id = self.instance_id
- self.context._project = self.projects[project_num]
- self.context.project_id = self.projects[project_num].id
- return self.network.allocate_fixed_ip(self.context, instance_id)
-
- def _deallocate_address(self, project_num, address):
- self.context._project = self.projects[project_num]
- self.context.project_id = self.projects[project_num].id
- self.network.deallocate_fixed_ip(self.context, address)
-
- def test_private_ipv6(self):
- """Make sure ipv6 is OK"""
- if FLAGS.use_ipv6:
- instance_ref = self._create_instance(0)
- address = self._create_address(0, instance_ref['id'])
- network_ref = db.project_get_network(
- context.get_admin_context(),
- self.context.project_id)
- address_v6 = db.instance_get_fixed_address_v6(
- context.get_admin_context(),
- instance_ref['id'])
- self.assertEqual(instance_ref['mac_address'],
- utils.to_mac(address_v6))
- instance_ref2 = db.fixed_ip_get_instance_v6(
- context.get_admin_context(),
- address_v6)
- self.assertEqual(instance_ref['id'], instance_ref2['id'])
- self.assertEqual(address_v6,
- utils.to_global_ipv6(
- network_ref['cidr_v6'],
- instance_ref['mac_address']))
- self._deallocate_address(0, address)
- db.instance_destroy(context.get_admin_context(),
- instance_ref['id'])
-
def test_public_network_association(self):
"""Makes sure that we can allocaate a public ip"""
# TODO(vish): better way of adding floating ips
@@ -157,24 +73,30 @@ class VlanNetworkTestCase(test.TestCase):
def test_allocate_deallocate_fixed_ip(self):
"""Makes sure that we can allocate and deallocate a fixed ip"""
address = self._create_address(0)
- self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
+ self.assertTrue(self._is_allocated_in_project(address,
+ self.projects[0].id))
lease_ip(address)
self._deallocate_address(0, address)
# Doesn't go away until it's dhcp released
- self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
+ self.assertTrue(self._is_allocated_in_project(address,
+ self.projects[0].id))
release_ip(address)
- self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
+ self.assertFalse(self._is_allocated_in_project(address,
+ self.projects[0].id))
def test_side_effects(self):
"""Ensures allocating and releasing has no side effects"""
address = self._create_address(0)
address2 = self._create_address(1, self.instance2_id)
- self.assertTrue(is_allocated_in_project(address, self.projects[0].id))
- self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
- self.assertFalse(is_allocated_in_project(address, self.projects[1].id))
+ self.assertTrue(self._is_allocated_in_project(address,
+ self.projects[0].id))
+ self.assertTrue(self._is_allocated_in_project(address2,
+ self.projects[1].id))
+ self.assertFalse(self._is_allocated_in_project(address,
+ self.projects[1].id))
# Addresses are allocated before they're issued
lease_ip(address)
@@ -182,14 +104,16 @@ class VlanNetworkTestCase(test.TestCase):
self._deallocate_address(0, address)
release_ip(address)
- self.assertFalse(is_allocated_in_project(address, self.projects[0].id))
+ self.assertFalse(self._is_allocated_in_project(address,
+ self.projects[0].id))
# First address release shouldn't affect the second
- self.assertTrue(is_allocated_in_project(address2, self.projects[1].id))
+ self.assertTrue(self._is_allocated_in_project(address2,
+ self.projects[1].id))
self._deallocate_address(1, address2)
release_ip(address2)
- self.assertFalse(is_allocated_in_project(address2,
+ self.assertFalse(self._is_allocated_in_project(address2,
self.projects[1].id))
def test_subnet_edge(self):
@@ -212,11 +136,11 @@ class VlanNetworkTestCase(test.TestCase):
lease_ip(address3)
self.context._project = self.projects[i]
self.context.project_id = self.projects[i].id
- self.assertFalse(is_allocated_in_project(address,
+ self.assertFalse(self._is_allocated_in_project(address,
self.projects[0].id))
- self.assertFalse(is_allocated_in_project(address2,
+ self.assertFalse(self._is_allocated_in_project(address2,
self.projects[0].id))
- self.assertFalse(is_allocated_in_project(address3,
+ self.assertFalse(self._is_allocated_in_project(address3,
self.projects[0].id))
self.network.deallocate_fixed_ip(self.context, address)
self.network.deallocate_fixed_ip(self.context, address2)
@@ -270,29 +194,6 @@ class VlanNetworkTestCase(test.TestCase):
self.network.deallocate_fixed_ip(self.context, address2)
release_ip(address)
- def test_available_ips(self):
- """Make sure the number of available ips for the network is correct
-
- The number of available IP addresses depends on the test
- environment's setup.
-
- Network size is set in test fixture's setUp method.
-
- There are ips reserved at the bottom and top of the range.
- services (network, gateway, CloudPipe, broadcast)
- """
- network = db.project_get_network(context.get_admin_context(),
- self.projects[0].id)
- net_size = flags.FLAGS.network_size
- admin_context = context.get_admin_context()
- total_ips = (db.network_count_available_ips(admin_context,
- network['id']) +
- db.network_count_reserved_ips(admin_context,
- network['id']) +
- db.network_count_allocated_ips(admin_context,
- network['id']))
- self.assertEqual(total_ips, net_size)
-
def test_too_many_addresses(self):
"""Test for a NoMoreAddresses exception when all fixed ips are used.
"""
@@ -325,49 +226,17 @@ class VlanNetworkTestCase(test.TestCase):
network['id'])
self.assertEqual(ip_count, num_available_ips)
+ def _is_allocated_in_project(self, address, project_id):
+ """Returns true if address is in specified project"""
+ project_net = db.project_get_network(context.get_admin_context(),
+ project_id)
+ network = db.fixed_ip_get_network(context.get_admin_context(),
+ address)
+ instance = db.fixed_ip_get_instance(context.get_admin_context(),
+ address)
+ # instance exists until release
+ return instance is not None and network['id'] == project_net['id']
-def is_allocated_in_project(address, project_id):
- """Returns true if address is in specified project"""
- project_net = db.project_get_network(context.get_admin_context(),
- project_id)
- network = db.fixed_ip_get_network(context.get_admin_context(), address)
- instance = db.fixed_ip_get_instance(context.get_admin_context(), address)
- # instance exists until release
- return instance is not None and network['id'] == project_net['id']
-
-
-def binpath(script):
- """Returns the absolute path to a script in bin"""
- return os.path.abspath(os.path.join(__file__, "../../../bin", script))
-
-
-def lease_ip(private_ip):
- """Run add command on dhcpbridge"""
- network_ref = db.fixed_ip_get_network(context.get_admin_context(),
- private_ip)
- instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
- private_ip)
- cmd = "%s add %s %s fake" % (binpath('nova-dhcpbridge'),
- instance_ref['mac_address'],
- private_ip)
- env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
- 'TESTING': '1',
- 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
- (out, err) = utils.execute(cmd, addl_env=env)
- LOG.debug("ISSUE_IP: %s, %s ", out, err)
-
-
-def release_ip(private_ip):
- """Run del command on dhcpbridge"""
- network_ref = db.fixed_ip_get_network(context.get_admin_context(),
- private_ip)
- instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
- private_ip)
- cmd = "%s del %s %s fake" % (binpath('nova-dhcpbridge'),
- instance_ref['mac_address'],
- private_ip)
- env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
- 'TESTING': '1',
- 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
- (out, err) = utils.execute(cmd, addl_env=env)
- LOG.debug("RELEASE_IP: %s, %s ", out, err)
+ def run(self, result=None):
+ if(FLAGS.network_manager == 'nova.network.manager.VlanManager'):
+ super(VlanNetworkTestCase, self).run(result)
--
cgit
From e4b176d41cca234082c28ba6d9188745f1d2b98a Mon Sep 17 00:00:00 2001
From: Cory Wright
Date: Wed, 9 Mar 2011 00:49:56 +0000
Subject: a few fixes for the tests
---
.../versions/009_add_os_type_to_instances.py | 53 ----------------------
.../versions/010_add_os_type_to_instances.py | 53 ++++++++++++++++++++++
nova/tests/test_xenapi.py | 1 +
nova/virt/xenapi/vmops.py | 2 +-
4 files changed, 55 insertions(+), 54 deletions(-)
delete mode 100644 nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/010_add_os_type_to_instances.py
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py b/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
deleted file mode 100644
index 514b92b81..000000000
--- a/nova/db/sqlalchemy/migrate_repo/versions/009_add_os_type_to_instances.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# 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.
-
-from sqlalchemy import *
-from sqlalchemy.sql import text
-from migrate import *
-
-from nova import log as logging
-
-
-meta = MetaData()
-
-instances = Table('instances', meta,
- Column('id', Integer(), primary_key=True, nullable=False),
- )
-
-instances_os_type = Column('os_type',
- String(length=255, convert_unicode=False,
- assert_unicode=None, unicode_error=None,
- _warn_on_bytestring=False),
- nullable=True)
-
-
-def upgrade(migrate_engine):
- # Upgrade operations go here. Don't create your own engine;
- # bind migrate_engine to your metadata
- meta.bind = migrate_engine
-
- instances.create_column(instances_os_type)
- migrate_engine.execute(instances.update()\
- .where(instances.c.os_type == None)\
- .values(os_type='linux'))
-
-
-def downgrade(migrate_engine):
- meta.bind = migrate_engine
-
- instances.drop_column('os_type')
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
new file mode 100644
index 000000000..514b92b81
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/010_add_os_type_to_instances.py
@@ -0,0 +1,53 @@
+# 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.
+
+from sqlalchemy import *
+from sqlalchemy.sql import text
+from migrate import *
+
+from nova import log as logging
+
+
+meta = MetaData()
+
+instances = Table('instances', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+instances_os_type = Column('os_type',
+ String(length=255, convert_unicode=False,
+ assert_unicode=None, unicode_error=None,
+ _warn_on_bytestring=False),
+ nullable=True)
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ instances.create_column(instances_os_type)
+ migrate_engine.execute(instances.update()\
+ .where(instances.c.os_type == None)\
+ .values(os_type='linux'))
+
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ instances.drop_column('os_type')
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 383819b00..cd125a301 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -440,6 +440,7 @@ class XenAPIMigrateInstance(test.TestCase):
'ramdisk_id': None,
'instance_type': 'm1.large',
'mac_address': 'aa:bb:cc:dd:ee:ff',
+ 'os_type': 'linux'
}
stubs.stub_out_migration_methods(self.stubs)
glance_stubs.stubout_glance_client(self.stubs,
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index a6a9fbf95..aa4372c3d 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -261,7 +261,7 @@ class VMOps(object):
template_vm_ref, template_vdi_uuids = self._get_snapshot(instance)
# call plugin to ship snapshot off to glance
VMHelper.upload_image(
- self._session, instance.id, template_vdi_uuids, image_id)
+ self._session, instance, template_vdi_uuids, image_id)
finally:
if template_vm_ref:
self._destroy(instance, template_vm_ref,
--
cgit
From f1dea606a64c9144fb723be0e5b86806891380f8 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Wed, 9 Mar 2011 11:54:21 -0500
Subject: add override to handle xml deserialization for server instance
creation
---
nova/api/openstack/servers.py | 68 +++++++-
nova/tests/api/openstack/fakes.py | 2 -
nova/tests/api/openstack/test_servers.py | 277 +++++++++++++++++++++++++++++++
3 files changed, 344 insertions(+), 3 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 93f504f91..4a6282204 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -17,6 +17,7 @@ import base64
import hashlib
import json
import traceback
+from xml.dom import minidom
from webob import exc
@@ -166,9 +167,16 @@ class Controller(wsgi.Controller):
personality_files.append((path, contents))
return personality_files
+ def _deserialize_create(self, request):
+ if request.content_type == "application/xml":
+ deserializer = ServerCreateRequestXMLDeserializer()
+ return deserializer.deserialize(request.body)
+ else:
+ return self._deserialize(request.body, request)
+
def create(self, req):
""" Creates a new server for a given user """
- env = self._deserialize(req.body, req)
+ env = self._deserialize_create(req)
if not env:
return faults.Fault(exc.HTTPUnprocessableEntity())
@@ -448,3 +456,61 @@ class Controller(wsgi.Controller):
_("Ramdisk not found for image %(image_id)s") % locals())
return kernel_id, ramdisk_id
+
+
+class ServerCreateRequestXMLDeserializer(object):
+
+ def deserialize(self, string):
+ dom = minidom.parseString(string)
+ server = self._extract_server(dom)
+ return {'server': server}
+
+ def _extract_server(self, node):
+ server = {}
+ server_node = self._find_first_child_named(node, 'server')
+ for attr in ["name", "imageId", "flavorId"]:
+ server[attr] = server_node.getAttribute(attr)
+ metadata = self._extract_metadata(server_node)
+ if metadata is not None:
+ server["metadata"] = metadata
+ personality = self._extract_personality(server_node)
+ if personality is not None:
+ server["personality"] = personality
+ return server
+
+ def _extract_metadata(self, server_node):
+ metadata_node = self._find_first_child_named(server_node, "metadata")
+ if metadata_node is None:
+ return None
+ metadata = {}
+ for meta_node in metadata_node.childNodes:
+ key = meta_node.getAttribute("key")
+ metadata[key] = self._extract_text(meta_node)
+ return metadata
+
+ def _extract_personality(self, server_node):
+ personality_node = \
+ self._find_first_child_named(server_node, "personality")
+ if personality_node is None:
+ return None
+ personality = []
+ for file_node in personality_node.childNodes:
+ item = {}
+ if file_node.hasAttribute("path"):
+ item["path"] = file_node.getAttribute("path")
+ item["contents"] = self._extract_text(file_node)
+ personality.append(item)
+ return personality
+
+ def _find_first_child_named(self, parent, name):
+ for node in parent.childNodes:
+ if node.nodeName == name:
+ return node
+ return None
+
+ def _extract_text(self, node):
+ if len(node.childNodes) == 1:
+ child = node.childNodes[0]
+ if child.nodeType == child.TEXT_NODE:
+ return child.nodeValue
+ return ""
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 49ce8c1b5..1d8692528 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -68,8 +68,6 @@ def fake_auth_init(self, application):
@webob.dec.wsgify
def fake_wsgi(self, req):
req.environ['nova.context'] = context.RequestContext(1, 1)
- if req.body:
- req.environ['inst_dict'] = json.loads(req.body)
return self.application
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 8fb5a9aec..5132d1ad3 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -18,6 +18,7 @@
import base64
import datetime
import json
+import unittest
import stubout
import webob
@@ -284,6 +285,37 @@ class ServersTest(test.TestCase):
response = request.get_response(fakes.wsgi_app())
return compute_api, response
+ def _format_xml_request_body(self, body_dict):
+ server = body_dict['server']
+ body_parts = []
+ body_parts.extend([
+ '',
+ '' % (
+ server['name'], server['imageId'], server['flavorId'])])
+ if 'metadata' in server:
+ metadata = server['metadata']
+ body_parts.append('')
+ for item in metadata.iteritems():
+ body_parts.append('%s' % item)
+ body_parts.append('')
+ if 'personality' in server:
+ personalities = server['personality']
+ body_parts.append('')
+ for file in personalities:
+ item = (file['path'], file['contents'])
+ body_parts.append('%s' % item)
+ body_parts.append('')
+ body_parts.append('')
+ return ''.join(body_parts)
+
+ def _get_create_request_xml(self, body_dict):
+ req = webob.Request.blank('/v1.0/servers')
+ req.content_type = 'application/xml'
+ req.method = 'POST'
+ req.body = self._format_xml_request_body(body_dict)
+ return req
+
def _create_instance_with_personality_json(self, personality):
body_dict = self._create_personality_request_dict(personality)
request = self._get_create_request_json(body_dict)
@@ -291,12 +323,25 @@ class ServersTest(test.TestCase):
self._run_create_instance_with_mock_compute_api(request)
return request, response, compute_api.personality_files
+ def _create_instance_with_personality_xml(self, personality):
+ body_dict = self._create_personality_request_dict(personality)
+ request = self._get_create_request_xml(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ return request, response, compute_api.personality_files
+
def test_create_instance_with_no_personality(self):
request, response, personality_files = \
self._create_instance_with_personality_json(personality=None)
self.assertEquals(response.status_int, 200)
self.assertEquals(personality_files, [])
+ def test_create_instance_with_no_personality_xml(self):
+ request, response, personality_files = \
+ self._create_instance_with_personality_xml(personality=None)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(personality_files, [])
+
def test_create_instance_with_personality(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Hello, World!"\n'
@@ -307,6 +352,16 @@ class ServersTest(test.TestCase):
self.assertEquals(response.status_int, 200)
self.assertEquals(personality_files, [(path, contents)])
+ def test_create_instance_with_personality_xml(self):
+ path = '/my/file/path'
+ contents = '#!/bin/bash\necho "Hello, World!"\n'
+ b64contents = base64.b64encode(contents)
+ personality = [(path, b64contents)]
+ request, response, personality_files = \
+ self._create_instance_with_personality_xml(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(personality_files, [(path, contents)])
+
def test_create_instance_with_personality_no_path(self):
personality = [('/remove/this/path',
base64.b64encode('my\n\file\ncontents'))]
@@ -318,6 +373,17 @@ class ServersTest(test.TestCase):
self.assertEquals(response.status_int, 400)
self.assertEquals(compute_api.personality_files, None)
+ def _test_create_instance_with_personality_no_path_xml(self):
+ personality = [('/remove/this/path',
+ base64.b64encode('my\n\file\ncontents'))]
+ body_dict = self._create_personality_request_dict(personality)
+ request = self._get_create_request_xml(body_dict)
+ request.body = request.body.replace(' path="/remove/this/path"', '')
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(compute_api.personality_files, None)
+
def test_create_instance_with_personality_no_contents(self):
personality = [('/test/path',
base64.b64encode('remove\nthese\ncontents'))]
@@ -605,3 +671,214 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '202 Accepted')
self.assertEqual(self.server_delete_called, True)
+
+
+class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
+
+ def setUp(self):
+ self.deserializer = servers.ServerCreateRequestXMLDeserializer()
+
+ def test_minimal_request(self):
+ serial_request = """
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"server": {
+ "name": "new-server-test",
+ "imageId": "1",
+ "flavorId": "1",
+ }}
+ self.assertEquals(request, expected)
+
+ def test_request_with_empty_metadata(self):
+ serial_request = """
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"server": {
+ "name": "new-server-test",
+ "imageId": "1",
+ "flavorId": "1",
+ "metadata": {},
+ }}
+ self.assertEquals(request, expected)
+
+ def test_request_with_empty_personality(self):
+ serial_request = """
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"server": {
+ "name": "new-server-test",
+ "imageId": "1",
+ "flavorId": "1",
+ "personality": [],
+ }}
+ self.assertEquals(request, expected)
+
+ def test_request_with_empty_metadata_and_personality(self):
+ serial_request = """
+\
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"server": {
+ "name": "new-server-test",
+ "imageId": "1",
+ "flavorId": "1",
+ "metadata": {},
+ "personality": [],
+ }}
+ self.assertEquals(request, expected)
+
+ def test_request_with_empty_metadata_and_personality_reversed(self):
+ serial_request = """
+\
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"server": {
+ "name": "new-server-test",
+ "imageId": "1",
+ "flavorId": "1",
+ "metadata": {},
+ "personality": [],
+ }}
+ self.assertEquals(request, expected)
+
+ def test_request_with_one_personality(self):
+ serial_request = """
+\
+aabbccdd"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = [{"path": "/etc/conf", "contents": "aabbccdd"}]
+ self.assertEquals(request["server"]["personality"], expected)
+
+ def test_request_with_two_personalities(self):
+ serial_request = """
+\
+aabbccdd\
+abcd"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = [{"path": "/etc/conf", "contents": "aabbccdd"},
+ {"path": "/etc/sudoers", "contents": "abcd"}]
+ self.assertEquals(request["server"]["personality"], expected)
+
+ def test_request_with_one_personality_missing_path(self):
+ serial_request = """
+\
+aabbccdd"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = [{"contents": "aabbccdd"}]
+ self.assertEquals(request["server"]["personality"], expected)
+
+ def test_request_with_one_personality_empty_contents(self):
+ serial_request = """
+\
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = [{"path": "/etc/conf", "contents": ""}]
+ self.assertEquals(request["server"]["personality"], expected)
+
+ def test_request_with_one_personality_empty_contents_variation(self):
+ serial_request = """
+\
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = [{"path": "/etc/conf", "contents": ""}]
+ self.assertEquals(request["server"]["personality"], expected)
+
+ def test_request_with_one_metadata(self):
+ serial_request = """
+\
+beta"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"alpha": "beta"}
+ self.assertEquals(request["server"]["metadata"], expected)
+
+ def test_request_with_two_metadata(self):
+ serial_request = """
+\
+betabar\
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"alpha": "beta", "foo": "bar"}
+ self.assertEquals(request["server"]["metadata"], expected)
+
+ def test_request_with_metadata_missing_value(self):
+ serial_request = """
+\
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"alpha": ""}
+ self.assertEquals(request["server"]["metadata"], expected)
+
+ def test_request_with_metadata_missing_key(self):
+ serial_request = """
+\
+beta"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"": "beta"}
+ self.assertEquals(request["server"]["metadata"], expected)
+
+ def test_request_with_metadata_duplicate_key(self):
+ serial_request = """
+\
+barbaz\
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"foo": "baz"}
+ self.assertEquals(request["server"]["metadata"], expected)
+
+ def test_canonical_request_from_docs(self):
+ serial_request = """
+\
+Apache1\
+\
+ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp\
+dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k\
+IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs\
+c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g\
+QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo\
+ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv\
+dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy\
+c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6\
+b25zLiINCg0KLVJpY2hhcmQgQmFjaA==\
+"""
+ expected = {"server": {
+ "name": "new-server-test",
+ "imageId": "1",
+ "flavorId": "1",
+ "metadata": {
+ "My Server Name": "Apache1",
+ },
+ "personality": [
+ {
+ "path": "/etc/banner.txt",
+ "contents": """\
+ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp\
+dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k\
+IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs\
+c2lvbi4uLnRoaXMgaXMgdGhlIHBsYWNlIHRvIGdvIG5vdy4g\
+QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo\
+ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv\
+dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy\
+c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6\
+b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""",
+ }
+ ],
+ }}
+ request = self.deserializer.deserialize(serial_request)
+ self.assertEqual(request, expected)
+
--
cgit
From 1d74816bf705cb672d9d323398b03142297d8bec Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Wed, 9 Mar 2011 12:03:17 -0500
Subject: remove superfluous trailing blank line
---
nova/tests/api/openstack/test_servers.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 5132d1ad3..51fb09b18 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -881,4 +881,3 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""",
}}
request = self.deserializer.deserialize(serial_request)
self.assertEqual(request, expected)
-
--
cgit
From 429fdb1ee733a62052c67f4e42c62447fc716ec0 Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Wed, 9 Mar 2011 18:10:45 +0000
Subject: removed uneeded **kw args leftover from removed account-in-url
changes.
---
nova/api/openstack/backup_schedules.py | 6 ++---
nova/api/openstack/consoles.py | 10 ++++----
nova/api/openstack/flavors.py | 6 ++---
nova/api/openstack/images.py | 12 +++++-----
nova/api/openstack/servers.py | 42 +++++++++++++++++-----------------
nova/api/openstack/shared_ip_groups.py | 12 +++++-----
nova/api/openstack/users.py | 12 +++++-----
nova/api/openstack/zones.py | 12 +++++-----
8 files changed, 56 insertions(+), 56 deletions(-)
diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py
index a4d5939df..7abb5f884 100644
--- a/nova/api/openstack/backup_schedules.py
+++ b/nova/api/openstack/backup_schedules.py
@@ -40,15 +40,15 @@ class Controller(wsgi.Controller):
def __init__(self):
pass
- def index(self, req, server_id, **kw):
+ def index(self, req, server_id):
""" Returns the list of backup schedules for a given instance """
return _translate_keys({})
- def create(self, req, server_id, **kw):
+ def create(self, req, server_id):
""" No actual update method required, since the existing API allows
both create and update through a POST """
return faults.Fault(exc.HTTPNotImplemented())
- def delete(self, req, server_id, id, **kw):
+ def delete(self, req, server_id, id):
""" Deletes an existing backup schedule """
return faults.Fault(exc.HTTPNotImplemented())
diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py
index 85b2a4140..9ebdbe710 100644
--- a/nova/api/openstack/consoles.py
+++ b/nova/api/openstack/consoles.py
@@ -55,7 +55,7 @@ class Controller(wsgi.Controller):
self.console_api = console.API()
super(Controller, self).__init__()
- def index(self, req, server_id, **kw):
+ def index(self, req, server_id):
"""Returns a list of consoles for this instance"""
consoles = self.console_api.get_consoles(
req.environ['nova.context'],
@@ -63,14 +63,14 @@ class Controller(wsgi.Controller):
return dict(consoles=[_translate_keys(console)
for console in consoles])
- def create(self, req, server_id, **kw):
+ def create(self, req, server_id):
"""Creates a new console"""
#info = self._deserialize(req.body, req)
self.console_api.create_console(
req.environ['nova.context'],
int(server_id))
- def show(self, req, server_id, id, **kw):
+ def show(self, req, server_id, id):
"""Shows in-depth information on a specific console"""
try:
console = self.console_api.get_console(
@@ -81,11 +81,11 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return _translate_detail_keys(console)
- def update(self, req, server_id, id, **kw):
+ def update(self, req, server_id, id):
"""You can't update a console"""
raise faults.Fault(exc.HTTPNotImplemented())
- def delete(self, req, server_id, id, **kw):
+ def delete(self, req, server_id, id):
"""Deletes a console"""
try:
self.console_api.delete_console(req.environ['nova.context'],
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index 1de67328b..f3d040ba3 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -34,17 +34,17 @@ class Controller(wsgi.Controller):
"attributes": {
"flavor": ["id", "name", "ram", "disk"]}}}
- def index(self, req, **kw):
+ def index(self, req):
"""Return all flavors in brief."""
return dict(flavors=[dict(id=flavor['id'], name=flavor['name'])
for flavor in self.detail(req)['flavors']])
- def detail(self, req, **kw):
+ def detail(self, req):
"""Return all flavors in detail."""
items = [self.show(req, id)['flavor'] for id in self._all_ids(req)]
return dict(flavors=items)
- def show(self, req, id, **kw):
+ def show(self, req, id):
"""Return data about the given flavor id."""
ctxt = req.environ['nova.context']
values = db.instance_type_get_by_flavor_id(ctxt, id)
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index 5bc5b9978..cf85a496f 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -115,14 +115,14 @@ class Controller(wsgi.Controller):
def __init__(self):
self._service = utils.import_object(FLAGS.image_service)
- def index(self, req, **kw):
+ def index(self, req):
"""Return all public images in brief"""
items = self._service.index(req.environ['nova.context'])
items = common.limited(items, req)
items = [_filter_keys(item, ('id', 'name')) for item in items]
return dict(images=items)
- def detail(self, req, **kw):
+ def detail(self, req):
"""Return all public images in detail"""
try:
items = self._service.detail(req.environ['nova.context'])
@@ -136,7 +136,7 @@ class Controller(wsgi.Controller):
items = [_translate_status(item) for item in items]
return dict(images=items)
- def show(self, req, id, **kw):
+ def show(self, req, id):
"""Return data about the given image id"""
image_id = common.get_image_id_from_image_hash(self._service,
req.environ['nova.context'], id)
@@ -145,11 +145,11 @@ class Controller(wsgi.Controller):
_convert_image_id_to_hash(image)
return dict(image=image)
- def delete(self, req, id, **kw):
+ def delete(self, req, id):
# Only public images are supported for now.
raise faults.Fault(exc.HTTPNotFound())
- def create(self, req, **kw):
+ def create(self, req):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
instance_id = env["image"]["serverId"]
@@ -160,7 +160,7 @@ class Controller(wsgi.Controller):
return dict(image=image_meta)
- def update(self, req, id, **kw):
+ def update(self, req, id):
# Users may not modify public images, and that's all that
# we support for now.
raise faults.Fault(exc.HTTPNotFound())
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 54060c2bb..c2bf42b72 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -105,11 +105,11 @@ class Controller(wsgi.Controller):
self._image_service = utils.import_object(FLAGS.image_service)
super(Controller, self).__init__()
- def index(self, req, **kw):
+ def index(self, req):
""" Returns a list of server names and ids for a given user """
return self._items(req, entity_maker=_translate_keys)
- def detail(self, req, **kw):
+ def detail(self, req):
""" Returns a list of server details for a given user """
return self._items(req, entity_maker=_translate_detail_keys)
@@ -123,7 +123,7 @@ class Controller(wsgi.Controller):
res = [entity_maker(inst)['server'] for inst in limited_list]
return dict(servers=res)
- def show(self, req, id, **kw):
+ def show(self, req, id):
""" Returns server details by server id """
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
@@ -131,7 +131,7 @@ class Controller(wsgi.Controller):
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
- def delete(self, req, id, **kw):
+ def delete(self, req, id):
""" Destroys a server """
try:
self.compute_api.delete(req.environ['nova.context'], id)
@@ -139,7 +139,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
- def create(self, req, **kw):
+ def create(self, req):
""" Creates a new server for a given user """
env = self._deserialize(req.body, req)
if not env:
@@ -180,7 +180,7 @@ class Controller(wsgi.Controller):
onset_files=env.get('onset_files', []))
return _translate_keys(instances[0])
- def update(self, req, id, **kw):
+ def update(self, req, id):
""" Updates the server name or password """
inst_dict = self._deserialize(req.body, req)
if not inst_dict:
@@ -202,7 +202,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPNoContent()
- def action(self, req, id, **kw):
+ def action(self, req, id):
"""Multi-purpose method used to reboot, rebuild, or
resize a server"""
@@ -267,7 +267,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def lock(self, req, id, **kw):
+ def lock(self, req, id):
"""
lock the instance with id
admin only operation
@@ -282,7 +282,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def unlock(self, req, id, **kw):
+ def unlock(self, req, id):
"""
unlock the instance with id
admin only operation
@@ -297,7 +297,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def get_lock(self, req, id, **kw):
+ def get_lock(self, req, id):
"""
return the boolean state of (instance with id)'s lock
@@ -311,7 +311,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def reset_network(self, req, id, **kw):
+ def reset_network(self, req, id):
"""
Reset networking on an instance (admin only).
@@ -325,7 +325,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def inject_network_info(self, req, id, **kw):
+ def inject_network_info(self, req, id):
"""
Inject network info for an instance (admin only).
@@ -339,7 +339,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def pause(self, req, id, **kw):
+ def pause(self, req, id):
""" Permit Admins to Pause the server. """
ctxt = req.environ['nova.context']
try:
@@ -350,7 +350,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def unpause(self, req, id, **kw):
+ def unpause(self, req, id):
""" Permit Admins to Unpause the server. """
ctxt = req.environ['nova.context']
try:
@@ -361,7 +361,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def suspend(self, req, id, **kw):
+ def suspend(self, req, id):
"""permit admins to suspend the server"""
context = req.environ['nova.context']
try:
@@ -372,7 +372,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def resume(self, req, id, **kw):
+ def resume(self, req, id):
"""permit admins to resume the server from suspend"""
context = req.environ['nova.context']
try:
@@ -383,7 +383,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def rescue(self, req, id, **kw):
+ def rescue(self, req, id):
"""Permit users to rescue the server."""
context = req.environ["nova.context"]
try:
@@ -394,7 +394,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def unrescue(self, req, id, **kw):
+ def unrescue(self, req, id):
"""Permit users to unrescue the server."""
context = req.environ["nova.context"]
try:
@@ -405,7 +405,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
- def get_ajax_console(self, req, id, **kw):
+ def get_ajax_console(self, req, id):
""" Returns a url to an instance's ajaxterm console. """
try:
self.compute_api.get_ajax_console(req.environ['nova.context'],
@@ -414,12 +414,12 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
- def diagnostics(self, req, id, **kw):
+ 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, **kw):
+ def actions(self, req, id):
"""Permit Admins to retrieve server actions."""
ctxt = req.environ["nova.context"]
items = self.compute_api.get_actions(ctxt, id)
diff --git a/nova/api/openstack/shared_ip_groups.py b/nova/api/openstack/shared_ip_groups.py
index e3c917749..5d78f9377 100644
--- a/nova/api/openstack/shared_ip_groups.py
+++ b/nova/api/openstack/shared_ip_groups.py
@@ -40,26 +40,26 @@ class Controller(wsgi.Controller):
'attributes': {
'sharedIpGroup': []}}}
- def index(self, req, **kw):
+ def index(self, req):
""" Returns a list of Shared IP Groups for the user """
return dict(sharedIpGroups=[])
- def show(self, req, id, **kw):
+ def show(self, req, id):
""" Shows in-depth information on a specific Shared IP Group """
return _translate_keys({})
- def update(self, req, id, **kw):
+ def update(self, req, id):
""" You can't update a Shared IP Group """
raise faults.Fault(exc.HTTPNotImplemented())
- def delete(self, req, id, **kw):
+ def delete(self, req, id):
""" Deletes a Shared IP Group """
raise faults.Fault(exc.HTTPNotImplemented())
- def detail(self, req, **kw):
+ def detail(self, req):
""" Returns a complete list of Shared IP Groups """
return _translate_detail_keys({})
- def create(self, req, **kw):
+ def create(self, req):
""" Creates a new Shared IP group """
raise faults.Fault(exc.HTTPNotImplemented())
diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py
index ae3bf7791..83ebec964 100644
--- a/nova/api/openstack/users.py
+++ b/nova/api/openstack/users.py
@@ -50,28 +50,28 @@ class Controller(wsgi.Controller):
if not context.is_admin:
raise exception.NotAuthorized(_("Not admin user"))
- def index(self, req, **kw):
+ def index(self, req):
"""Return all users in brief"""
users = self.manager.get_users()
users = common.limited(users, req)
users = [_translate_keys(user) for user in users]
return dict(users=users)
- def detail(self, req, **kw):
+ def detail(self, req):
"""Return all users in detail"""
return self.index(req)
- def show(self, req, id, **kw):
+ def show(self, req, id):
"""Return data about the given user id"""
user = self.manager.get_user(id)
return dict(user=_translate_keys(user))
- def delete(self, req, id, **kw):
+ def delete(self, req, id):
self._check_admin(req.environ['nova.context'])
self.manager.delete_user(id)
return {}
- def create(self, req, **kw):
+ def create(self, req):
self._check_admin(req.environ['nova.context'])
env = self._deserialize(req.body, req)
is_admin = env['user'].get('admin') in ('T', 'True', True)
@@ -81,7 +81,7 @@ class Controller(wsgi.Controller):
user = self.manager.create_user(name, access, secret, is_admin)
return dict(user=_translate_keys(user))
- def update(self, req, id, **kw):
+ def update(self, req, id):
self._check_admin(req.environ['nova.context'])
env = self._deserialize(req.body, req)
is_admin = env['user'].get('admin')
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
index 30bf2b67b..d5206da20 100644
--- a/nova/api/openstack/zones.py
+++ b/nova/api/openstack/zones.py
@@ -43,35 +43,35 @@ class Controller(wsgi.Controller):
"attributes": {
"zone": ["id", "api_url"]}}}
- def index(self, req, **kw):
+ def index(self, req):
"""Return all zones in brief"""
items = db.zone_get_all(req.environ['nova.context'])
items = common.limited(items, req)
items = [_scrub_zone(item) for item in items]
return dict(zones=items)
- def detail(self, req, **kw):
+ def detail(self, req):
"""Return all zones in detail"""
return self.index(req)
- def show(self, req, id, **kw):
+ def show(self, req, id):
"""Return data about the given zone id"""
zone_id = int(id)
zone = db.zone_get(req.environ['nova.context'], zone_id)
return dict(zone=_scrub_zone(zone))
- def delete(self, req, id, **kw):
+ def delete(self, req, id):
zone_id = int(id)
db.zone_delete(req.environ['nova.context'], zone_id)
return {}
- def create(self, req, **kw):
+ def create(self, req):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
zone = db.zone_create(context, env["zone"])
return dict(zone=_scrub_zone(zone))
- def update(self, req, id, **kw):
+ def update(self, req, id):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
zone_id = int(id)
--
cgit
From 1166e16d08769222189e31e6de1c6019495fc743 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Wed, 9 Mar 2011 13:54:30 -0500
Subject: move my tests into their own testcase
---
nova/tests/api/openstack/test_servers.py | 447 +++++++++++++++++--------------
1 file changed, 243 insertions(+), 204 deletions(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index f2fbb04af..b0f888766 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -238,210 +238,6 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
- def _setup_mock_compute_api_for_personality(self):
-
- class MockComputeAPI(object):
-
- def __init__(self):
- self.personality_files = None
-
- def create(self, *args, **kwargs):
- if 'personality_files' in kwargs:
- self.personality_files = kwargs['personality_files']
- else:
- self.personality_files = None
- return [{'id': '1234', 'display_name': 'fakeinstance'}]
-
- def make_stub_method(canned_return):
- def stub_method(*args, **kwargs):
- return canned_return
- return stub_method
-
- compute_api = MockComputeAPI()
- self.stubs.Set(nova.compute, 'API', make_stub_method(compute_api))
- self.stubs.Set(nova.api.openstack.servers.Controller,
- '_get_kernel_ramdisk_from_image', make_stub_method((1, 1)))
- self.stubs.Set(nova.api.openstack.common,
- 'get_image_id_from_image_hash', make_stub_method(2))
- return compute_api
-
- def _create_personality_request_dict(self, personality_files):
- server = {}
- server['name'] = 'new-server-test'
- server['imageId'] = 1
- server['flavorId'] = 1
- if personality_files is not None:
- personalities = []
- for path, contents in personality_files:
- personalities.append({'path': path, 'contents': contents})
- server['personality'] = personalities
- return {'server': server}
-
- def _get_create_request_json(self, body_dict):
- req = webob.Request.blank('/v1.0/servers')
- req.content_type = 'application/json'
- req.method = 'POST'
- req.body = json.dumps(body_dict)
- return req
-
- def _run_create_instance_with_mock_compute_api(self, request):
- compute_api = self._setup_mock_compute_api_for_personality()
- response = request.get_response(fakes.wsgi_app())
- return compute_api, response
-
- def _format_xml_request_body(self, body_dict):
- server = body_dict['server']
- body_parts = []
- body_parts.extend([
- '',
- '' % (
- server['name'], server['imageId'], server['flavorId'])])
- if 'metadata' in server:
- metadata = server['metadata']
- body_parts.append('')
- for item in metadata.iteritems():
- body_parts.append('%s' % item)
- body_parts.append('')
- if 'personality' in server:
- personalities = server['personality']
- body_parts.append('')
- for file in personalities:
- item = (file['path'], file['contents'])
- body_parts.append('%s' % item)
- body_parts.append('')
- body_parts.append('')
- return ''.join(body_parts)
-
- def _get_create_request_xml(self, body_dict):
- req = webob.Request.blank('/v1.0/servers')
- req.content_type = 'application/xml'
- req.method = 'POST'
- req.body = self._format_xml_request_body(body_dict)
- return req
-
- def _create_instance_with_personality_json(self, personality):
- body_dict = self._create_personality_request_dict(personality)
- request = self._get_create_request_json(body_dict)
- compute_api, response = \
- self._run_create_instance_with_mock_compute_api(request)
- return request, response, compute_api.personality_files
-
- def _create_instance_with_personality_xml(self, personality):
- body_dict = self._create_personality_request_dict(personality)
- request = self._get_create_request_xml(body_dict)
- compute_api, response = \
- self._run_create_instance_with_mock_compute_api(request)
- return request, response, compute_api.personality_files
-
- def test_create_instance_with_no_personality(self):
- request, response, personality_files = \
- self._create_instance_with_personality_json(personality=None)
- self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [])
-
- def test_create_instance_with_no_personality_xml(self):
- request, response, personality_files = \
- self._create_instance_with_personality_xml(personality=None)
- self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [])
-
- def test_create_instance_with_personality(self):
- path = '/my/file/path'
- contents = '#!/bin/bash\necho "Hello, World!"\n'
- b64contents = base64.b64encode(contents)
- personality = [(path, b64contents)]
- request, response, personality_files = \
- self._create_instance_with_personality_json(personality)
- self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [(path, contents)])
-
- def test_create_instance_with_personality_xml(self):
- path = '/my/file/path'
- contents = '#!/bin/bash\necho "Hello, World!"\n'
- b64contents = base64.b64encode(contents)
- personality = [(path, b64contents)]
- request, response, personality_files = \
- self._create_instance_with_personality_xml(personality)
- self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [(path, contents)])
-
- def test_create_instance_with_personality_no_path(self):
- personality = [('/remove/this/path',
- base64.b64encode('my\n\file\ncontents'))]
- body_dict = self._create_personality_request_dict(personality)
- del body_dict['server']['personality'][0]['path']
- request = self._get_create_request_json(body_dict)
- compute_api, response = \
- self._run_create_instance_with_mock_compute_api(request)
- self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.personality_files, None)
-
- def _test_create_instance_with_personality_no_path_xml(self):
- personality = [('/remove/this/path',
- base64.b64encode('my\n\file\ncontents'))]
- body_dict = self._create_personality_request_dict(personality)
- request = self._get_create_request_xml(body_dict)
- request.body = request.body.replace(' path="/remove/this/path"', '')
- compute_api, response = \
- self._run_create_instance_with_mock_compute_api(request)
- self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.personality_files, None)
-
- def test_create_instance_with_personality_no_contents(self):
- personality = [('/test/path',
- base64.b64encode('remove\nthese\ncontents'))]
- body_dict = self._create_personality_request_dict(personality)
- del body_dict['server']['personality'][0]['contents']
- request = self._get_create_request_json(body_dict)
- compute_api, response = \
- self._run_create_instance_with_mock_compute_api(request)
- self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.personality_files, None)
-
- def test_create_instance_with_personality_not_a_list(self):
- personality = [('/test/path', base64.b64encode('test\ncontents\n'))]
- body_dict = self._create_personality_request_dict(personality)
- body_dict['server']['personality'] = \
- body_dict['server']['personality'][0]
- request = self._get_create_request_json(body_dict)
- compute_api, response = \
- self._run_create_instance_with_mock_compute_api(request)
- self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.personality_files, None)
-
- def test_create_instance_with_personality_with_non_b64_content(self):
- path = '/my/file/path'
- contents = '#!/bin/bash\necho "Oh no!"\n'
- personality = [(path, contents)]
- request, response, personality_files = \
- self._create_instance_with_personality_json(personality)
- self.assertEquals(response.status_int, 400)
- self.assertEquals(personality_files, None)
-
- def test_create_instance_with_three_personalities(self):
- files = [
- ('/etc/sudoers', 'ALL ALL=NOPASSWD: ALL\n'),
- ('/etc/motd', 'Enjoy your root access!\n'),
- ('/etc/dovecot.conf', 'dovecot\nconfig\nstuff\n'),
- ]
- personality = []
- for path, content in files:
- personality.append((path, base64.b64encode(content)))
- request, response, personality_files = \
- self._create_instance_with_personality_json(personality)
- self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, files)
-
- def test_create_instance_personality_empty_content(self):
- path = '/my/file/path'
- contents = ''
- personality = [(path, contents)]
- request, response, personality_files = \
- self._create_instance_with_personality_json(personality)
- self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [(path, contents)])
-
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
@@ -980,5 +776,248 @@ b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""",
self.assertEqual(request, expected)
+class TestServerInstanceCreation(test.TestCase):
+
+ def setUp(self):
+ super(TestServerInstanceCreation, self).setUp()
+ self.stubs = stubout.StubOutForTesting()
+ fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthDatabase.data = {}
+ fakes.stub_out_networking(self.stubs)
+ fakes.stub_out_rate_limiting(self.stubs)
+ fakes.stub_out_auth(self.stubs)
+ fakes.stub_out_key_pair_funcs(self.stubs)
+ fakes.stub_out_image_service(self.stubs)
+ self.stubs.Set(nova.db.api, 'instance_get_all', return_servers)
+ self.stubs.Set(nova.db.api, 'instance_get', return_server)
+ self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
+ return_servers)
+ self.stubs.Set(nova.db.api, 'instance_add_security_group',
+ return_security_group)
+ self.stubs.Set(nova.db.api, 'instance_update', instance_update)
+ self.stubs.Set(nova.db.api, 'instance_get_fixed_address',
+ instance_address)
+ self.stubs.Set(nova.db.api, 'instance_get_floating_address',
+ instance_address)
+ self.stubs.Set(nova.compute.API, 'pause', fake_compute_api)
+ self.stubs.Set(nova.compute.API, 'unpause', fake_compute_api)
+ self.stubs.Set(nova.compute.API, 'suspend', fake_compute_api)
+ self.stubs.Set(nova.compute.API, 'resume', fake_compute_api)
+ self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api)
+ self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api)
+ self.allow_admin = FLAGS.allow_admin_api
+
+ self.webreq = common.webob_factory('/v1.0/servers')
+
+ def tearDown(self):
+ self.stubs.UnsetAll()
+ FLAGS.allow_admin_api = self.allow_admin
+ super(TestServerInstanceCreation, self).tearDown()
+
+ def _setup_mock_compute_api_for_personality(self):
+
+ class MockComputeAPI(object):
+
+ def __init__(self):
+ self.personality_files = None
+
+ def create(self, *args, **kwargs):
+ if 'personality_files' in kwargs:
+ self.personality_files = kwargs['personality_files']
+ else:
+ self.personality_files = None
+ return [{'id': '1234', 'display_name': 'fakeinstance'}]
+
+ def make_stub_method(canned_return):
+ def stub_method(*args, **kwargs):
+ return canned_return
+ return stub_method
+
+ compute_api = MockComputeAPI()
+ self.stubs.Set(nova.compute, 'API', make_stub_method(compute_api))
+ self.stubs.Set(nova.api.openstack.servers.Controller,
+ '_get_kernel_ramdisk_from_image', make_stub_method((1, 1)))
+ self.stubs.Set(nova.api.openstack.common,
+ 'get_image_id_from_image_hash', make_stub_method(2))
+ return compute_api
+
+ def _create_personality_request_dict(self, personality_files):
+ server = {}
+ server['name'] = 'new-server-test'
+ server['imageId'] = 1
+ server['flavorId'] = 1
+ if personality_files is not None:
+ personalities = []
+ for path, contents in personality_files:
+ personalities.append({'path': path, 'contents': contents})
+ server['personality'] = personalities
+ return {'server': server}
+
+ def _get_create_request_json(self, body_dict):
+ req = webob.Request.blank('/v1.0/servers')
+ req.content_type = 'application/json'
+ req.method = 'POST'
+ req.body = json.dumps(body_dict)
+ return req
+
+ def _run_create_instance_with_mock_compute_api(self, request):
+ compute_api = self._setup_mock_compute_api_for_personality()
+ response = request.get_response(fakes.wsgi_app())
+ return compute_api, response
+
+ def _format_xml_request_body(self, body_dict):
+ server = body_dict['server']
+ body_parts = []
+ body_parts.extend([
+ '',
+ '' % (
+ server['name'], server['imageId'], server['flavorId'])])
+ if 'metadata' in server:
+ metadata = server['metadata']
+ body_parts.append('')
+ for item in metadata.iteritems():
+ body_parts.append('%s' % item)
+ body_parts.append('')
+ if 'personality' in server:
+ personalities = server['personality']
+ body_parts.append('')
+ for file in personalities:
+ item = (file['path'], file['contents'])
+ body_parts.append('%s' % item)
+ body_parts.append('')
+ body_parts.append('')
+ return ''.join(body_parts)
+
+ def _get_create_request_xml(self, body_dict):
+ req = webob.Request.blank('/v1.0/servers')
+ req.content_type = 'application/xml'
+ req.method = 'POST'
+ req.body = self._format_xml_request_body(body_dict)
+ return req
+
+ def _create_instance_with_personality_json(self, personality):
+ body_dict = self._create_personality_request_dict(personality)
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ return request, response, compute_api.personality_files
+
+ def _create_instance_with_personality_xml(self, personality):
+ body_dict = self._create_personality_request_dict(personality)
+ request = self._get_create_request_xml(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ return request, response, compute_api.personality_files
+
+ def test_create_instance_with_no_personality(self):
+ request, response, personality_files = \
+ self._create_instance_with_personality_json(personality=None)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(personality_files, [])
+
+ def test_create_instance_with_no_personality_xml(self):
+ request, response, personality_files = \
+ self._create_instance_with_personality_xml(personality=None)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(personality_files, [])
+
+ def test_create_instance_with_personality(self):
+ path = '/my/file/path'
+ contents = '#!/bin/bash\necho "Hello, World!"\n'
+ b64contents = base64.b64encode(contents)
+ personality = [(path, b64contents)]
+ request, response, personality_files = \
+ self._create_instance_with_personality_json(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(personality_files, [(path, contents)])
+
+ def test_create_instance_with_personality_xml(self):
+ path = '/my/file/path'
+ contents = '#!/bin/bash\necho "Hello, World!"\n'
+ b64contents = base64.b64encode(contents)
+ personality = [(path, b64contents)]
+ request, response, personality_files = \
+ self._create_instance_with_personality_xml(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(personality_files, [(path, contents)])
+
+ def test_create_instance_with_personality_no_path(self):
+ personality = [('/remove/this/path',
+ base64.b64encode('my\n\file\ncontents'))]
+ body_dict = self._create_personality_request_dict(personality)
+ del body_dict['server']['personality'][0]['path']
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(compute_api.personality_files, None)
+
+ def _test_create_instance_with_personality_no_path_xml(self):
+ personality = [('/remove/this/path',
+ base64.b64encode('my\n\file\ncontents'))]
+ body_dict = self._create_personality_request_dict(personality)
+ request = self._get_create_request_xml(body_dict)
+ request.body = request.body.replace(' path="/remove/this/path"', '')
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(compute_api.personality_files, None)
+
+ def test_create_instance_with_personality_no_contents(self):
+ personality = [('/test/path',
+ base64.b64encode('remove\nthese\ncontents'))]
+ body_dict = self._create_personality_request_dict(personality)
+ del body_dict['server']['personality'][0]['contents']
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(compute_api.personality_files, None)
+
+ def test_create_instance_with_personality_not_a_list(self):
+ personality = [('/test/path', base64.b64encode('test\ncontents\n'))]
+ body_dict = self._create_personality_request_dict(personality)
+ body_dict['server']['personality'] = \
+ body_dict['server']['personality'][0]
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(compute_api.personality_files, None)
+
+ def test_create_instance_with_personality_with_non_b64_content(self):
+ path = '/my/file/path'
+ contents = '#!/bin/bash\necho "Oh no!"\n'
+ personality = [(path, contents)]
+ request, response, personality_files = \
+ self._create_instance_with_personality_json(personality)
+ self.assertEquals(response.status_int, 400)
+ self.assertEquals(personality_files, None)
+
+ def test_create_instance_with_three_personalities(self):
+ files = [
+ ('/etc/sudoers', 'ALL ALL=NOPASSWD: ALL\n'),
+ ('/etc/motd', 'Enjoy your root access!\n'),
+ ('/etc/dovecot.conf', 'dovecot\nconfig\nstuff\n'),
+ ]
+ personality = []
+ for path, content in files:
+ personality.append((path, base64.b64encode(content)))
+ request, response, personality_files = \
+ self._create_instance_with_personality_json(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(personality_files, files)
+
+ def test_create_instance_personality_empty_content(self):
+ path = '/my/file/path'
+ contents = ''
+ personality = [(path, contents)]
+ request, response, personality_files = \
+ self._create_instance_with_personality_json(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(personality_files, [(path, contents)])
+
+
if __name__ == "__main__":
unittest.main()
--
cgit
From 3999bb363501c6587f75255333094c9e61bf1828 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Wed, 9 Mar 2011 14:07:33 -0500
Subject: remove unneeded stubs
---
nova/tests/api/openstack/test_servers.py | 22 ----------------------
1 file changed, 22 deletions(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index b0f888766..c67ecdaae 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -783,32 +783,10 @@ class TestServerInstanceCreation(test.TestCase):
self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthDatabase.data = {}
- fakes.stub_out_networking(self.stubs)
- fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_auth(self.stubs)
fakes.stub_out_key_pair_funcs(self.stubs)
- fakes.stub_out_image_service(self.stubs)
- self.stubs.Set(nova.db.api, 'instance_get_all', return_servers)
- self.stubs.Set(nova.db.api, 'instance_get', return_server)
- self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
- return_servers)
- self.stubs.Set(nova.db.api, 'instance_add_security_group',
- return_security_group)
- self.stubs.Set(nova.db.api, 'instance_update', instance_update)
- self.stubs.Set(nova.db.api, 'instance_get_fixed_address',
- instance_address)
- self.stubs.Set(nova.db.api, 'instance_get_floating_address',
- instance_address)
- self.stubs.Set(nova.compute.API, 'pause', fake_compute_api)
- self.stubs.Set(nova.compute.API, 'unpause', fake_compute_api)
- self.stubs.Set(nova.compute.API, 'suspend', fake_compute_api)
- self.stubs.Set(nova.compute.API, 'resume', fake_compute_api)
- self.stubs.Set(nova.compute.API, "get_diagnostics", fake_compute_api)
- self.stubs.Set(nova.compute.API, "get_actions", fake_compute_api)
self.allow_admin = FLAGS.allow_admin_api
- self.webreq = common.webob_factory('/v1.0/servers')
-
def tearDown(self):
self.stubs.UnsetAll()
FLAGS.allow_admin_api = self.allow_admin
--
cgit
From 7854cf996608c09e753c70c3914746dab22c4e1a Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Wed, 9 Mar 2011 14:21:18 -0500
Subject: update authors file
---
Authors | 1 +
1 file changed, 1 insertion(+)
diff --git a/Authors b/Authors
index 7993955e2..583cf8e75 100644
--- a/Authors
+++ b/Authors
@@ -39,6 +39,7 @@ Ken Pepple
Kevin L. Mitchell
Koji Iida
Lorin Hochstein
+Mark Washenberger
Masanori Itoh
Matt Dietz
Michael Gundlach
--
cgit
From 2c733d5365b753989b506d82d376d980cd701547 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Wed, 9 Mar 2011 14:38:34 -0500
Subject: rearrange functions and add docstrings
---
nova/api/openstack/servers.py | 83 ++++++++++++++++++++++++++-----------------
1 file changed, 50 insertions(+), 33 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 4a18d870c..419a001fb 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -141,39 +141,6 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
- def _get_personality_files(self, personality):
- """
- Create a list of personality files from the personality attribute
-
- At this time, personality_files must be formatted as a list of
- (file_path, file_content) pairs for compatibility with the
- underlying compute service.
- """
- personality_files = []
- for item in personality:
- try:
- path = item['path']
- contents = item['contents']
- except KeyError as key:
- expl = 'Bad personality format: missing %s' % key
- raise exc.HTTPBadRequest(explanation=expl)
- except TypeError:
- raise exc.HTTPBadRequest(explanation='Bad personality format')
- try:
- contents = base64.b64decode(contents)
- except TypeError:
- msg = 'Personality content for %s cannot be decoded' % path
- raise exc.HTTPBadRequest(explanation=msg)
- personality_files.append((path, contents))
- return personality_files
-
- def _deserialize_create(self, request):
- if request.content_type == "application/xml":
- deserializer = ServerCreateRequestXMLDeserializer()
- return deserializer.deserialize(request.body)
- else:
- return self._deserialize(request.body, request)
-
def create(self, req):
""" Creates a new server for a given user """
env = self._deserialize_create(req)
@@ -218,6 +185,44 @@ class Controller(wsgi.Controller):
personality_files=personality_files)
return _translate_keys(instances[0])
+ def _deserialize_create(self, request):
+ """
+ Deserialize a create request
+
+ Overrides normal behavior in the case of xml content
+ """
+ if request.content_type == "application/xml":
+ deserializer = ServerCreateRequestXMLDeserializer()
+ return deserializer.deserialize(request.body)
+ else:
+ return self._deserialize(request.body, request)
+
+ def _get_personality_files(self, personality):
+ """
+ Create a list of personality files from the personality attribute
+
+ At this time, personality_files must be formatted as a list of
+ (file_path, file_content) pairs for compatibility with the
+ underlying compute service.
+ """
+ personality_files = []
+ for item in personality:
+ try:
+ path = item['path']
+ contents = item['contents']
+ except KeyError as key:
+ expl = 'Bad personality format: missing %s' % key
+ raise exc.HTTPBadRequest(explanation=expl)
+ except TypeError:
+ raise exc.HTTPBadRequest(explanation='Bad personality format')
+ try:
+ contents = base64.b64decode(contents)
+ except TypeError:
+ msg = 'Personality content for %s cannot be decoded' % path
+ raise exc.HTTPBadRequest(explanation=msg)
+ personality_files.append((path, contents))
+ return personality_files
+
def update(self, req, id):
""" Updates the server name or password """
inst_dict = self._deserialize(req.body, req)
@@ -507,13 +512,21 @@ class Controller(wsgi.Controller):
class ServerCreateRequestXMLDeserializer(object):
+ """
+ Deserializer to handle xml-formatted server create requests.
+
+ Handles standard server attributes as well as optional metadata
+ and personality attributes
+ """
def deserialize(self, string):
+ """Deserialize an xml-formatted server create request"""
dom = minidom.parseString(string)
server = self._extract_server(dom)
return {'server': server}
def _extract_server(self, node):
+ """Marshal the server attribute of a parsed request"""
server = {}
server_node = self._find_first_child_named(node, 'server')
for attr in ["name", "imageId", "flavorId"]:
@@ -527,6 +540,7 @@ class ServerCreateRequestXMLDeserializer(object):
return server
def _extract_metadata(self, server_node):
+ """Marshal the metadata attribute of a parsed request"""
metadata_node = self._find_first_child_named(server_node, "metadata")
if metadata_node is None:
return None
@@ -537,6 +551,7 @@ class ServerCreateRequestXMLDeserializer(object):
return metadata
def _extract_personality(self, server_node):
+ """Marshal the personality attribute of a parsed request"""
personality_node = \
self._find_first_child_named(server_node, "personality")
if personality_node is None:
@@ -551,12 +566,14 @@ class ServerCreateRequestXMLDeserializer(object):
return personality
def _find_first_child_named(self, parent, name):
+ """Search a nodes children for the first child with a given name"""
for node in parent.childNodes:
if node.nodeName == name:
return node
return None
def _extract_text(self, node):
+ """Get the text field contained by the given node"""
if len(node.childNodes) == 1:
child = node.childNodes[0]
if child.nodeType == child.TEXT_NODE:
--
cgit
From 75f7a73735957d5ddf04c7c9a23decf1a6fa7f9f Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Wed, 9 Mar 2011 14:55:36 -0600
Subject: Added naming scheme comment
---
nova/virt/xenapi_conn.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py
index b63a5f8c3..bfe290be3 100644
--- a/nova/virt/xenapi_conn.py
+++ b/nova/virt/xenapi_conn.py
@@ -49,6 +49,12 @@ reactor thread if the VM.get_by_name_label or VM.get_record calls block.
address for the nova-volume host
:target_port: iSCSI Target Port, 3260 Default
:iqn_prefix: IQN Prefix, e.g. 'iqn.2010-10.org.openstack'
+
+**Variable Naming Scheme**
+
+- suffix "_ref" for opaque references
+- suffix "_uuid" for UUIDs
+- suffix "_rec" for record objects
"""
import sys
--
cgit
From fb4785b85c1bef4179140cfb85ce01eca9fb5da5 Mon Sep 17 00:00:00 2001
From: Cory Wright
Date: Wed, 9 Mar 2011 21:46:27 +0000
Subject: fix the copyright notice in migration
---
.../sqlalchemy/migrate_repo/versions/010_add_os_type_to_instances.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
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 514b92b81..eb3066894 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
@@ -1,8 +1,6 @@
# 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.
+# Copyright 2010 OpenStack LLC.
#
# 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
--
cgit
From 21937b48fcac81fa108f37f307b1b2e969bb7b4f Mon Sep 17 00:00:00 2001
From: "Kevin L. Mitchell"
Date: Thu, 10 Mar 2011 00:01:15 +0000
Subject: Replace session.execute() calls performing raw UPDATE statements with
SQLAlchemy code, with the exception of fixed_ip_disassociate_all_by_timeout()
---
nova/db/sqlalchemy/api.py | 97 ++++++++++++++++++++++++++++++-----------------
1 file changed, 62 insertions(+), 35 deletions(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 5e498fc6f..22c85106d 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -701,14 +701,18 @@ def instance_data_get_for_project(context, project_id):
def instance_destroy(context, instance_id):
session = get_session()
with session.begin():
- session.execute('update instances set deleted=1,'
- 'deleted_at=:at where id=:id',
- {'id': instance_id,
- 'at': datetime.datetime.utcnow()})
- session.execute('update security_group_instance_association '
- 'set deleted=1,deleted_at=:at where instance_id=:id',
- {'id': instance_id,
- 'at': datetime.datetime.utcnow()})
+ session.query(models.Instance).\
+ filter_by(id=instance_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': models.Instance.updated_at + 0})
+ session.query(models.SecurityGroupInstanceAssociation).\
+ filter_by(instance_id=instance_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at':
+ (models.SecurityGroupInstanceAssociation.
+ updated_at + 0)})
@require_context
@@ -950,9 +954,11 @@ def key_pair_destroy_all_by_user(context, user_id):
authorize_user_context(context, user_id)
session = get_session()
with session.begin():
- # TODO(vish): do we have to use sql here?
- session.execute('update key_pairs set deleted=1 where user_id=:id',
- {'id': user_id})
+ session.query(models.KeyPair).\
+ filter_by(user_id=user_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': models.KeyPair.updated_at + 0})
@require_context
@@ -1063,7 +1069,9 @@ def network_disassociate(context, network_id):
@require_admin_context
def network_disassociate_all(context):
session = get_session()
- session.execute('update networks set project_id=NULL')
+ session.query(models.Network).\
+ update({'project_id': None,
+ 'updated_at': models.Network.updated_at + 0})
@require_context
@@ -1433,15 +1441,17 @@ def volume_data_get_for_project(context, project_id):
def volume_destroy(context, volume_id):
session = get_session()
with session.begin():
- # TODO(vish): do we have to use sql here?
- session.execute('update volumes set deleted=1 where id=:id',
- {'id': volume_id})
- session.execute('update export_devices set volume_id=NULL '
- 'where volume_id=:id',
- {'id': volume_id})
- session.execute('update iscsi_targets set volume_id=NULL '
- 'where volume_id=:id',
- {'id': volume_id})
+ session.query(models.Volume).\
+ filter_by(id=volume_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': models.Volume.updated_at + 0})
+ session.query(models.ExportDevice).\
+ filter_by(volume_id=volume_id).\
+ update({'volume_id': None})
+ session.query(models.IscsiTarget).\
+ filter_by(volume_id=volume_id).\
+ update({'volume_id': None})
@require_admin_context
@@ -1661,17 +1671,26 @@ def security_group_create(context, values):
def security_group_destroy(context, security_group_id):
session = get_session()
with session.begin():
- # TODO(vish): do we have to use sql here?
- session.execute('update security_groups set deleted=1 where id=:id',
- {'id': security_group_id})
- session.execute('update security_group_instance_association '
- 'set deleted=1,deleted_at=:at '
- 'where security_group_id=:id',
- {'id': security_group_id,
- 'at': datetime.datetime.utcnow()})
- session.execute('update security_group_rules set deleted=1 '
- 'where group_id=:id',
- {'id': security_group_id})
+ session.query(models.SecurityGroup).\
+ filter_by(id=security_group_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at':
+ models.SecurityGroup.updated_at + 0})
+ session.query(models.SecurityGroupInstanceAssociation).\
+ filter_by(security_group_id=security_group_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at':
+ (models.SecurityGroupInstanceAssocation.
+ updated_at + 0)})
+ session.query(models.SecurityGroupIngressRule).\
+ filter_by(group_id=security_group_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at':
+ (models.SecurityGroupIngressRule.
+ updated_at + 0)})
@require_context
@@ -1679,9 +1698,17 @@ def security_group_destroy_all(context, session=None):
if not session:
session = get_session()
with session.begin():
- # TODO(vish): do we have to use sql here?
- session.execute('update security_groups set deleted=1')
- session.execute('update security_group_rules set deleted=1')
+ session.query(models.SecurityGroup).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at':
+ models.SecurityGroup.updated_at + 0})
+ session.query(models.SecurityGroupIngressRule).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at':
+ (models.SecurityGroupIngressRule.
+ updated_at + 0)})
###################
--
cgit
From f0bb6d9fc47b92d335c7d7fa238dfd43f0dbdf69 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Thu, 10 Mar 2011 13:30:52 +0900
Subject: fixed based on reviewer's comment.
---
bin/nova-manage | 8 ++---
nova/compute/manager.py | 22 ++++++-------
nova/db/sqlalchemy/api.py | 4 +--
nova/scheduler/driver.py | 10 +++---
nova/tests/test_compute.py | 24 +++++++-------
nova/tests/test_scheduler.py | 5 +--
nova/tests/test_service.py | 77 +++++++++++++++++---------------------------
nova/tests/test_virt.py | 6 ++--
nova/tests/test_volume.py | 7 ++--
nova/virt/libvirt_conn.py | 22 ++++++++-----
nova/volume/driver.py | 2 +-
11 files changed, 89 insertions(+), 98 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index d782f6028..f9e4fa8dc 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -567,7 +567,7 @@ class VmCommands(object):
if (FLAGS.volume_driver != 'nova.volume.driver.AOEDriver' and \
FLAGS.volume_driver != 'nova.volume.driver.ISCSIDriver'):
msg = _("Support only AOEDriver and ISCSIDriver. Sorry!")
- raise exception.Error(msg)
+ raise exception.Error(msg)
rpc.call(ctxt,
FLAGS.scheduler_topic,
@@ -637,8 +637,8 @@ class ServiceCommands(object):
"args": {"host": host}})
if type(result) != dict:
- print 'Unexpected error occurs'
- print '[Result]', result
+ print _('An unexpected error has occurred.')
+ print _('[Result]'), result
else:
cpu = result['resource']['vcpus']
mem = result['resource']['memory_mb']
@@ -667,7 +667,7 @@ class ServiceCommands(object):
ctxt = context.get_admin_context()
service_refs = db.service_get_all_by_host(ctxt, host)
if len(service_refs) <= 0:
- raise exception.Invalid(_('%s does not exists.') % host)
+ raise exception.Invalid(_('%s does not exist.') % host)
service_refs = [s for s in service_refs if s['topic'] == 'compute']
if len(service_refs) <= 0:
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 3675cc92e..0cab10fc3 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -64,7 +64,7 @@ flags.DEFINE_integer('password_length', 12,
flags.DEFINE_string('console_host', socket.gethostname(),
'Console proxy host to use to connect to instances on'
'this host.')
-flags.DEFINE_string('live_migration_retry_count', 30,
+flags.DEFINE_integer('live_migration_retry_count', 30,
("Retry count needed in live_migration."
" sleep 1 sec for each count"))
@@ -757,8 +757,9 @@ class ComputeManager(manager.Manager):
dirpath = FLAGS.instances_path
fd, tmp_file = tempfile.mkstemp(dir=dirpath)
LOG.debug(_("Creating tmpfile %s to notify to other "
- "compute node that they mounts same storage.") % tmp_file)
- os.fdopen(fd, 'w+').close()
+ "compute nodes that they should mount "
+ "the same storage.") % tmp_file)
+ os.close(fd)
return os.path.basename(tmp_file)
@exception.wrap_exception
@@ -812,7 +813,7 @@ class ComputeManager(manager.Manager):
# Getting fixed ips
fixed_ip = self.db.instance_get_fixed_address(context, instance_id)
if not fixed_ip:
- msg = _("%(instance_id)s(%(ec2_id)s) does'nt have fixed_ip")
+ msg = _("%(instance_id)s(%(ec2_id)s) does not have fixed_ip.")
raise exception.NotFound(msg % locals())
# If any volume is mounted, prepare here.
@@ -929,7 +930,7 @@ class ComputeManager(manager.Manager):
floating_ip = self.db.instance_get_floating_address(ctxt,
instance_id)
if not floating_ip:
- LOG.info(_('floating_ip is not found for %s'), i_name)
+ LOG.info(_('No floating_ip is found for %s.'), i_name)
else:
floating_ip_ref = self.db.floating_ip_get_by_address(ctxt,
floating_ip)
@@ -937,7 +938,7 @@ class ComputeManager(manager.Manager):
floating_ip_ref['address'],
{'host': dest})
except exception.NotFound:
- LOG.info(_('Floating_ip is not found for %s'), i_name)
+ LOG.info(_('No floating_ip is found for %s.'), i_name)
except:
LOG.error(_("Live migration: Unexpected error:"
"%s cannot inherit floating ip..") % i_name)
@@ -945,12 +946,11 @@ class ComputeManager(manager.Manager):
# Restore instance/volume state
self.recover_live_migration(ctxt, instance_ref, dest)
- LOG.info(_('Migrating %(i_name)s to %(dest)s finishes successfully.')
+ LOG.info(_('Migrating %(i_name)s to %(dest)s finished successfully.')
% locals())
- LOG.info(_("The below error is normally occurs. "
- "Just check if instance is successfully migrated.\n"
- "libvir: QEMU error : Domain not found: no domain "
- "with matching name.."))
+ LOG.info(_("You may see the error \"libvirt: QEMU error: "
+ "Domain not found: no domain with matching name.\" "
+ "This error can be safely ignored."))
def recover_live_migration(self, ctxt, instance_ref, host=None):
"""Recovers Instance/volume state from migrating -> running.
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 8ea5062ae..f44ca0fa3 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -192,8 +192,8 @@ def service_get_all_compute_by_host(context, host):
all()
if not result:
- raise exception.NotFound(_("%s does not exist or not "
- "compute node.") % host)
+ raise exception.NotFound(_("%s does not exist or is not "
+ "a compute node.") % host)
return result
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index 791f9000d..ed3dfe1c0 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -226,7 +226,6 @@ class Scheduler(object):
"args": {'cpu_info': oservice_ref['cpu_info']}})
except rpc.RemoteError:
- ec2_id = instance_ref['hostname']
src = instance_ref['host']
logging.exception(_("host %(dest)s is not compatible with "
"original host %(src)s.") % locals())
@@ -259,9 +258,10 @@ class Scheduler(object):
mem_avail = mem_total - mem_used
mem_inst = instance_ref['memory_mb']
if mem_avail <= mem_inst:
- raise exception.NotEmpty(_("%(ec2_id)s is not capable to "
- "migrate %(dest)s (host:%(mem_avail)s "
- " <= instance:%(mem_inst)s)")
+ raise exception.NotEmpty(_("Unable to migrate %(ec2_id)s "
+ "to destination: %(dest)s "
+ "(host:%(mem_avail)s "
+ "<= instance:%(mem_inst)s)")
% locals())
def mounted_on_same_shared_storage(self, context, instance_ref, dest):
@@ -292,7 +292,7 @@ class Scheduler(object):
except rpc.RemoteError:
ipath = FLAGS.instances_path
- logging.error(_("Cannot comfirm tmpfile at %(ipath)s is on "
+ logging.error(_("Cannot confirm tmpfile at %(ipath)s is on "
"same shared storage between %(src)s "
"and %(dest)s.") % locals())
raise
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 85c2c948b..71899ba9e 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -89,14 +89,14 @@ class ComputeTestCase(test.TestCase):
Use this when any testcase executed later than test_run_terminate
"""
vol1 = models.Volume()
- vol1.__setitem__('id', 1)
+ vol1['id'] = 1
vol2 = models.Volume()
- vol2.__setitem__('id', 2)
+ vol2['id'] = 2
instance_ref = models.Instance()
- instance_ref.__setitem__('id', 1)
- instance_ref.__setitem__('volumes', [vol1, vol2])
- instance_ref.__setitem__('hostname', 'i-00000001')
- instance_ref.__setitem__('host', 'dummy')
+ instance_ref['id'] = 1
+ instance_ref['volumes'] = [vol1, vol2]
+ instance_ref['hostname'] = 'i-00000001'
+ instance_ref['host'] = 'dummy'
return instance_ref
def test_create_instance_defaults_display_name(self):
@@ -114,9 +114,9 @@ class ComputeTestCase(test.TestCase):
"""Make sure create associates security groups"""
group = self._create_group()
instance_ref = models.Instance()
- instance_ref.__setitem__('id', 1)
- instance_ref.__setitem__('volumes', [{'id': 1}, {'id': 2}])
- instance_ref.__setitem__('hostname', 'i-00000001')
+ instance_ref['id'] = 1
+ instance_ref['volumes'] = [{'id': 1}, {'id': 2}]
+ instance_ref['hostname'] = 'i-00000001'
return instance_ref
def test_create_instance_defaults_display_name(self):
@@ -390,7 +390,7 @@ class ComputeTestCase(test.TestCase):
def test_pre_live_migration_instance_has_no_volume(self):
"""Confirm log meg when instance doesn't mount any volumes."""
i_ref = self._get_dummy_instance()
- i_ref.__setitem__('volumes', [])
+ i_ref['volumes'] = []
c = context.get_admin_context()
self._setup_other_managers()
@@ -501,7 +501,7 @@ class ComputeTestCase(test.TestCase):
def test_live_migration_dest_raises_exception_no_volume(self):
"""Same as above test(input pattern is different) """
i_ref = self._get_dummy_instance()
- i_ref.__setitem__('volumes', [])
+ i_ref['volumes'] = []
c = context.get_admin_context()
topic = db.queue_get_for(c, FLAGS.compute_topic, i_ref['host'])
@@ -526,7 +526,7 @@ class ComputeTestCase(test.TestCase):
def test_live_migration_works_correctly_no_volume(self):
"""Confirm live_migration() works as expected correctly."""
i_ref = self._get_dummy_instance()
- i_ref.__setitem__('volumes', [])
+ i_ref['volumes'] = []
c = context.get_admin_context()
topic = db.queue_get_for(c, FLAGS.compute_topic, i_ref['host'])
diff --git a/nova/tests/test_scheduler.py b/nova/tests/test_scheduler.py
index 711b66af7..8ac02c5a4 100644
--- a/nova/tests/test_scheduler.py
+++ b/nova/tests/test_scheduler.py
@@ -119,7 +119,8 @@ class SchedulerTestCase(test.TestCase):
try:
scheduler.show_host_resources(ctxt, dest)
except exception.NotFound, e:
- c1 = (0 <= e.message.find('does not exist or not compute node'))
+ c1 = (e.message.find(_("does not exist or is not a "
+ "compute node.")) >= 0)
self.assertTrue(c1)
def _dic_is_equal(self, dic1, dic2, keys=None):
@@ -786,7 +787,7 @@ class SimpleDriverTestCase(test.TestCase):
i_ref,
'somewhere')
except exception.NotEmpty, e:
- c = (e.message.find('is not capable to migrate') >= 0)
+ c = (e.message.find('Unable to migrate') >= 0)
self.assertTrue(c)
db.instance_destroy(self.context, instance_id)
diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py
index d17f6a22a..666c4a11d 100644
--- a/nova/tests/test_service.py
+++ b/nova/tests/test_service.py
@@ -42,24 +42,6 @@ class FakeManager(manager.Manager):
def test_method(self):
return 'manager'
-# temporary variable to store host/binary/self.mox
-# from each method to fake class.
-global_host = None
-global_binary = None
-global_mox = None
-
-
-class FakeComputeManager(compute_manager.ComputeManager):
- """Fake computemanager manager for tests"""
-
- def __init__(self, compute_driver=None, *args, **kwargs):
- global ghost, gbinary, gmox
- self.update_available_resource(mox.IgnoreArg())
- gmox.ReplayAll()
- super(FakeComputeManager, self).__init__(compute_driver,
- *args,
- **kwargs)
-
class ExtendedService(service.Service):
def test_method(self):
@@ -275,37 +257,38 @@ class ServiceTestCase(test.TestCase):
"""Confirm compute updates their record of compute-service table."""
host = 'foo'
binary = 'nova-compute'
- topic = 'compute1'
- service_create = {'host': host,
- 'binary': binary,
- 'topic': topic,
- 'report_count': 0,
- 'availability_zone': 'nova'}
- service_ref = {'host': host,
- 'binary': binary,
- 'topic': topic,
- 'report_count': 0,
- 'availability_zone': 'nova',
- 'id': 1}
-
- service.db.service_get_by_args(mox.IgnoreArg(),
- host,
- binary).AndRaise(exception.NotFound())
- service.db.service_create(mox.IgnoreArg(),
- service_create).AndReturn(service_ref)
- self.mox.StubOutWithMock(compute_manager.ComputeManager,
- 'update_available_resource')
-
- global ghost, gbinary, gmox
- ghost = host
- gbinary = binary
- gmox = self.mox
-
+ topic = 'compute'
+
+ # Any mocks are not working without UnsetStubs() here.
+ self.mox.UnsetStubs()
+ ctxt = context.get_admin_context()
+ service_ref = db.service_create(ctxt, {'host': host,
+ 'binary': binary,
+ 'topic': topic})
serv = service.Service(host,
binary,
topic,
- 'nova.tests.test_service.FakeComputeManager')
- # ReplayAll has been executed FakeComputeManager.__init__()
- #self.mox.ReplayAll()
+ 'nova.compute.manager.ComputeManager')
+
+ # This testcase want to test calling update_available_resource.
+ # No need to call periodic call, then below variable must be set 0.
+ serv.report_interval = 0
+ serv.periodic_interval = 0
+
+ # Creating mocks
+ self.mox.StubOutWithMock(service.rpc.Connection, 'instance')
+ service.rpc.Connection.instance(new=mox.IgnoreArg())
+ service.rpc.Connection.instance(new=mox.IgnoreArg())
+ self.mox.StubOutWithMock(serv.manager.driver,
+ 'update_available_resource')
+ serv.manager.driver.update_available_resource(mox.IgnoreArg(), host)
+
+ # Just doing start()-stop(), not confirm new db record is created,
+ # because update_available_resource() works only in libvirt environment.
+ # This testcase confirms update_available_resource() is called.
+ # Otherwise, mox complains.
+ self.mox.ReplayAll()
serv.start()
serv.stop()
+
+ db.service_destroy(ctxt, service_ref['id'])
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index 7ea8c0fb5..ee41ae732 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -283,7 +283,7 @@ class LibvirtConnTestCase(test.TestCase):
self.assertEquals(uri, testuri)
db.instance_destroy(user_context, instance_ref['id'])
- def tes1t_update_available_resource_works_correctly(self):
+ def test_update_available_resource_works_correctly(self):
"""Confirm compute_node table is updated successfully."""
org_path = FLAGS.instances_path = ''
FLAGS.instances_path = '.'
@@ -314,7 +314,7 @@ class LibvirtConnTestCase(test.TestCase):
compute_node = service_ref['compute_node'][0]
if sys.platform.upper() == 'LINUX2':
- self.assertTrue(compute_node['vcpus'] > 0)
+ self.assertTrue(compute_node['vcpus'] >= 0)
self.assertTrue(compute_node['memory_mb'] > 0)
self.assertTrue(compute_node['local_gb'] > 0)
self.assertTrue(compute_node['vcpus_used'] == 0)
@@ -323,7 +323,7 @@ class LibvirtConnTestCase(test.TestCase):
self.assertTrue(len(compute_node['hypervisor_type']) > 0)
self.assertTrue(compute_node['hypervisor_version'] > 0)
else:
- self.assertTrue(compute_node['vcpus'] > 0)
+ self.assertTrue(compute_node['vcpus'] >= 0)
self.assertTrue(compute_node['memory_mb'] == 0)
self.assertTrue(compute_node['local_gb'] > 0)
self.assertTrue(compute_node['vcpus_used'] == 0)
diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py
index e8b4ceee8..d88e363da 100644
--- a/nova/tests/test_volume.py
+++ b/nova/tests/test_volume.py
@@ -284,9 +284,10 @@ class AOETestCase(DriverTestCase):
self.volume.check_for_export(self.context, self.instance_id)
except exception.ProcessExecutionError, e:
volume_id = volume_id_list[0]
- msg = _("""Cannot confirm exported volume id:%(volume_id)s."""
- """vblade process for e%(shelf_id)s.%(blade_id)s """
- """isn't running.""") % locals()
+ msg = _("Cannot confirm exported volume id:%(volume_id)s. "
+ "vblade process for e%(shelf_id)s.%(blade_id)s "
+ "isn't running.") % locals()
+
msg_is_match = (0 <= e.message.find(msg))
self.assertTrue(msg_is_match)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 938719a7c..43a9dc4e7 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -860,7 +860,14 @@ class LibvirtConnection(object):
"""
- return multiprocessing.cpu_count()
+ # On certain platforms, this will raise a NotImplementedError.
+ try:
+ return multiprocessing.cpu_count()
+ except NotImplementedError:
+ LOG.warn(_("Cannot get the number of cpu, because this "
+ "function is not implemented for this platform. "
+ "This error can be safely ignored for now."))
+ return 0
def get_memory_mb_total(self):
"""Get the total memory size(MB) of physical computer.
@@ -1042,9 +1049,9 @@ class LibvirtConnection(object):
try:
service_ref = db.service_get_all_compute_by_host(ctxt, host)[0]
except exception.NotFound:
- msg = _(("""Cannot update compute manager specific info,"""
- """ Because no service record found."""))
- raise exception.Invalid(msg)
+ raise exception.Invalid(_("Cannot update compute manager "
+ "specific info, because no service "
+ "record was found."))
# Updating host information
dic = {'vcpus': self.get_vcpu_total(),
@@ -1059,11 +1066,11 @@ class LibvirtConnection(object):
compute_node_ref = service_ref['compute_node']
if not compute_node_ref:
- LOG.info(_('Compute_service record is created for %s ') % host)
+ LOG.info(_('Compute_service record created for %s ') % host)
dic['service_id'] = service_ref['id']
db.compute_node_create(ctxt, dic)
else:
- LOG.info(_('Compute_service record is updated for %s ') % host)
+ LOG.info(_('Compute_service record updated for %s ') % host)
db.compute_node_update(ctxt, compute_node_ref[0]['id'], dic)
def compare_cpu(self, cpu_info):
@@ -1081,8 +1088,7 @@ class LibvirtConnection(object):
"""
- LOG.info(_('Checking cpu_info: instance was launched this cpu.\n%s')
- % cpu_info)
+ LOG.info(_('Instance launched has CPU info:\n%s') % cpu_info)
dic = utils.loads(cpu_info)
xml = str(Template(self.cpuinfo_xml, searchList=dic))
LOG.info(_('to xml...\n:%s ' % xml))
diff --git a/nova/volume/driver.py b/nova/volume/driver.py
index a902da6ac..31a6a02ee 100644
--- a/nova/volume/driver.py
+++ b/nova/volume/driver.py
@@ -234,7 +234,7 @@ class AOEDriver(VolumeDriver):
break
if not exported:
# Instance will be terminated in this case.
- desc = _("Cannot confirm exported volume id:%(volume_id)s."
+ desc = _("Cannot confirm exported volume id:%(volume_id)s. "
"vblade process for e%(shelf_id)s.%(blade_id)s "
"isn't running.") % locals()
raise exception.ProcessExecutionError(out, _err, cmd=cmd,
--
cgit
From 90f38451e5df4f0ca862401cf898f01ffede6174 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Thu, 10 Mar 2011 00:26:25 -0500
Subject: add tests to verify the serialization of adminPass in server creation
response
---
nova/tests/api/openstack/test_servers.py | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 2fc28fe67..0561ad499 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -19,6 +19,7 @@ import base64
import datetime
import json
import unittest
+from xml.dom import minidom
import stubout
import webob
@@ -908,6 +909,7 @@ class TestServerInstanceCreation(test.TestCase):
def _get_create_request_xml(self, body_dict):
req = webob.Request.blank('/v1.0/servers')
req.content_type = 'application/xml'
+ req.accept = 'application/xml'
req.method = 'POST'
req.body = self._format_xml_request_body(body_dict)
return req
@@ -1034,6 +1036,23 @@ class TestServerInstanceCreation(test.TestCase):
self.assertEquals(response.status_int, 200)
self.assertEquals(personality_files, [(path, contents)])
+ def test_create_instance_admin_pass_json(self):
+ request, response, dummy = \
+ self._create_instance_with_personality_json(None)
+ self.assertEquals(response.status_int, 200)
+ response = json.loads(response.body)
+ self.assertTrue('adminPass' in response['server'])
+ self.assertTrue(response['server']['adminPass'].startswith('fake'))
+
+ def test_create_instance_admin_pass_xml(self):
+ request, response, dummy = \
+ self._create_instance_with_personality_xml(None)
+ self.assertEquals(response.status_int, 200)
+ dom = minidom.parseString(response.body)
+ server = dom.childNodes[0]
+ self.assertEquals(server.nodeName, 'server')
+ self.assertTrue(server.getAttribute('adminPass').startswith('fake'))
+
if __name__ == "__main__":
unittest.main()
--
cgit
From b75ab789194f1ced801b1d68ae8cc54051716414 Mon Sep 17 00:00:00 2001
From: Kei Masumoto
Date: Thu, 10 Mar 2011 15:16:03 +0900
Subject: fix pep8 check
---
nova/tests/test_service.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/tests/test_service.py b/nova/tests/test_service.py
index 666c4a11d..393f9d20b 100644
--- a/nova/tests/test_service.py
+++ b/nova/tests/test_service.py
@@ -284,9 +284,9 @@ class ServiceTestCase(test.TestCase):
serv.manager.driver.update_available_resource(mox.IgnoreArg(), host)
# Just doing start()-stop(), not confirm new db record is created,
- # because update_available_resource() works only in libvirt environment.
- # This testcase confirms update_available_resource() is called.
- # Otherwise, mox complains.
+ # because update_available_resource() works only in
+ # libvirt environment. This testcase confirms
+ # update_available_resource() is called. Otherwise, mox complains.
self.mox.ReplayAll()
serv.start()
serv.stop()
--
cgit
From e76aad24ce8a9b1b7de1b2f874c22c9995f3071f Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 10 Mar 2011 14:30:17 +0100
Subject: Only include ramdisk and kernel id if they are actually set.
---
nova/api/ec2/cloud.py | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index b1917e9ea..1d2254225 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -147,8 +147,6 @@ class CloudController(object):
instance_ref['id'])
ec2_id = ec2utils.id_to_ec2_id(instance_ref['id'])
image_ec2_id = self._image_ec2_id(instance_ref['image_id'], 'machine')
- k_ec2_id = self._image_ec2_id(instance_ref['kernel_id'], 'kernel')
- r_ec2_id = self._image_ec2_id(instance_ref['ramdisk_id'], 'ramdisk')
data = {
'user-data': base64.b64decode(instance_ref['user_data']),
'meta-data': {
@@ -167,8 +165,6 @@ class CloudController(object):
'instance-type': instance_ref['instance_type'],
'local-hostname': hostname,
'local-ipv4': address,
- 'kernel-id': k_ec2_id,
- 'ramdisk-id': r_ec2_id,
'placement': {'availability-zone': availability_zone},
'public-hostname': hostname,
'public-ipv4': floating_ip or '',
@@ -176,6 +172,13 @@ class CloudController(object):
'reservation-id': instance_ref['reservation_id'],
'security-groups': '',
'mpi': mpi}}
+
+ for image_type in ['kernel', 'ramdisk']:
+ if '%s_id' % image_type in instance_ref:
+ ec2_id = self._image_ec2_id(instance_ref['%s_id' % image_type],
+ image_type)
+ data['meta-data']['%s-id' % image_type] = ec2_id
+
if False: # TODO(vish): store ancestor ids
data['ancestor-ami-ids'] = []
if False: # TODO(vish): store product codes
--
cgit
From 801212a0ff04ddc33719d17b8c8ca847db5b1228 Mon Sep 17 00:00:00 2001
From: Cory Wright
Date: Thu, 10 Mar 2011 15:47:55 +0000
Subject: Use a FLAGS.default_os_type if available
---
nova/virt/xenapi/vm_utils.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index a1b85284f..8dd246178 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -41,9 +41,11 @@ from nova.virt.xenapi import HelperBase
from nova.virt.xenapi.volume_utils import StorageError
-FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.virt.xenapi.vm_utils")
+FLAGS = flags.FLAGS
+flags.DEFINE_string('default_os_type', 'linux', 'Default OS type')
+
XENAPI_POWER_STATE = {
'Halted': power_state.SHUTDOWN,
'Running': power_state.RUNNING,
@@ -347,7 +349,7 @@ class VMHelper(HelperBase):
logging.debug(_("Asking xapi to upload %(vdi_uuids)s as"
" ID %(image_id)s") % locals())
- os_type = instance.os_type and instance.os_type or 'linux'
+ os_type = instance.os_type or FLAGS.default_os_type
params = {'vdi_uuids': vdi_uuids,
'image_id': image_id,
--
cgit
From b361153a160ba1d61ed1d52de419cd27a8b4feda Mon Sep 17 00:00:00 2001
From: "Kevin L. Mitchell"
Date: Thu, 10 Mar 2011 16:42:13 +0000
Subject: Correct a misspelling
---
nova/db/sqlalchemy/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 22c85106d..2b60ab7d5 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1682,7 +1682,7 @@ def security_group_destroy(context, security_group_id):
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
'updated_at':
- (models.SecurityGroupInstanceAssocation.
+ (models.SecurityGroupInstanceAssociation.
updated_at + 0)})
session.query(models.SecurityGroupIngressRule).\
filter_by(group_id=security_group_id).\
--
cgit
From 4ead485ab69ee1e92635857ba73133a9e1d3bbcb Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Thu, 10 Mar 2011 12:06:09 -0600
Subject: Cleaned up vmops
---
nova/virt/xenapi/vm_utils.py | 26 +++++------
nova/virt/xenapi/vmops.py | 104 ++++++++++++++++++++-----------------------
2 files changed, 61 insertions(+), 69 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index ce081a2d6..4ad820bcd 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -90,7 +90,7 @@ class VMHelper(HelperBase):
get_instance_type(instance.instance_type)
mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
vcpus = str(instance_type['vcpus'])
- rec = {
+ vm_rec = {
'name_label': instance.name,
'name_description': '',
'is_a_template': False,
@@ -122,23 +122,23 @@ class VMHelper(HelperBase):
#Complete VM configuration record according to the image type
#non-raw/raw with PV kernel/raw in HVM mode
if instance.kernel_id:
- rec['PV_bootloader'] = ''
- rec['PV_kernel'] = kernel
- rec['PV_ramdisk'] = ramdisk
- rec['PV_args'] = 'root=/dev/xvda1'
- rec['PV_bootloader_args'] = ''
- rec['PV_legacy_args'] = ''
+ vm_rec['PV_bootloader'] = ''
+ vm_rec['PV_kernel'] = kernel
+ vm_rec['PV_ramdisk'] = ramdisk
+ vm_rec['PV_args'] = 'root=/dev/xvda1'
+ vm_rec['PV_bootloader_args'] = ''
+ vm_rec['PV_legacy_args'] = ''
else:
if pv_kernel:
- rec['PV_args'] = 'noninteractive'
- rec['PV_bootloader'] = 'pygrub'
+ vm_rec['PV_args'] = 'noninteractive'
+ vm_rec['PV_bootloader'] = 'pygrub'
else:
- rec['HVM_boot_policy'] = 'BIOS order'
- rec['HVM_boot_params'] = {'order': 'dc'}
- rec['platform'] = {'acpi': 'true', 'apic': 'true',
+ vm_rec['HVM_boot_policy'] = 'BIOS order'
+ vm_rec['HVM_boot_params'] = {'order': 'dc'}
+ vm_rec['platform'] = {'acpi': 'true', 'apic': 'true',
'pae': 'true', 'viridian': 'true'}
LOG.debug(_('Created VM %s...'), instance.name)
- vm_ref = session.call_xenapi('VM.create', rec)
+ vm_ref = session.call_xenapi('VM.create', vm_rec)
instance_name = instance.name
LOG.debug(_('Created VM %(instance_name)s as %(vm_ref)s.') % locals())
return vm_ref
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 562ecd4d5..5375df5b4 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -56,10 +56,10 @@ class VMOps(object):
def list_instances(self):
"""List VM instances"""
vms = []
- for vm in self._session.get_xenapi().VM.get_all():
- rec = self._session.get_xenapi().VM.get_record(vm)
- if not rec["is_a_template"] and not rec["is_control_domain"]:
- vms.append(rec["name_label"])
+ for vm_ref in self._session.get_xenapi().VM.get_all():
+ vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
+ if not vm_rec["is_a_template"] and not vm_rec["is_control_domain"]:
+ vms.append(vm_rec["name_label"])
return vms
def _start(self, instance, vm_ref=None):
@@ -371,8 +371,8 @@ class VMOps(object):
def reboot(self, instance):
"""Reboot VM instance"""
- vm = self._get_vm_opaque_ref(instance)
- task = self._session.call_xenapi('Async.VM.clean_reboot', vm)
+ vm_ref = self._get_vm_opaque_ref(instance)
+ task = self._session.call_xenapi('Async.VM.clean_reboot', vm_ref)
self._session.wait_for_task(task, instance.id)
def set_admin_password(self, instance, new_pass):
@@ -571,26 +571,27 @@ class VMOps(object):
def pause(self, instance, callback):
"""Pause VM instance"""
- vm = self._get_vm_opaque_ref(instance)
- task = self._session.call_xenapi('Async.VM.pause', vm)
+ vm_ref = self._get_vm_opaque_ref(instance)
+ task = self._session.call_xenapi('Async.VM.pause', vm_ref)
self._wait_with_callback(instance.id, task, callback)
def unpause(self, instance, callback):
"""Unpause VM instance"""
- vm = self._get_vm_opaque_ref(instance)
- task = self._session.call_xenapi('Async.VM.unpause', vm)
+ vm_ref = self._get_vm_opaque_ref(instance)
+ task = self._session.call_xenapi('Async.VM.unpause', vm_ref)
self._wait_with_callback(instance.id, task, callback)
def suspend(self, instance, callback):
"""suspend the specified instance"""
- vm = self._get_vm_opaque_ref(instance)
- task = self._session.call_xenapi('Async.VM.suspend', vm)
+ vm_ref = self._get_vm_opaque_ref(instance)
+ task = self._session.call_xenapi('Async.VM.suspend', vm_ref)
self._wait_with_callback(instance.id, task, callback)
def resume(self, instance, callback):
"""resume the specified instance"""
- vm = self._get_vm_opaque_ref(instance)
- task = self._session.call_xenapi('Async.VM.resume', vm, False, True)
+ vm_ref = self._get_vm_opaque_ref(instance)
+ task = self._session.call_xenapi('Async.VM.resume', vm_ref, False,
+ True)
self._wait_with_callback(instance.id, task, callback)
def rescue(self, instance, callback):
@@ -605,22 +606,18 @@ class VMOps(object):
raise RuntimeError(_(
"Instance is already in Rescue Mode: %s" % instance.name))
- vm = self._get_vm_opaque_ref(instance)
- self._shutdown(instance, vm)
- self._acquire_bootlock(vm)
+ vm_ref = self._get_vm_opaque_ref(instance)
+ self._shutdown(instance, vm_ref)
+ self._acquire_bootlock(vm_ref)
instance._rescue = True
self.spawn(instance)
- rescue_vm = self._get_vm_opaque_ref(instance)
+ rescue_vm_ref = self._get_vm_opaque_ref(instance)
- vbd = self._session.get_xenapi().VM.get_VBDs(vm)[0]
+ vbd = self._session.get_xenapi().VM.get_VBDs(vm_ref)[0]
vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"]
- vbd_ref = VMHelper.create_vbd(
- self._session,
- rescue_vm,
- vdi_ref,
- 1,
- False)
+ vbd_ref = VMHelper.create_vbd(self._session, rescue_vm_ref, vdi_ref,
+ 1, False)
self._session.call_xenapi("Async.VBD.plug", vbd_ref)
@@ -637,7 +634,7 @@ class VMOps(object):
raise exception.NotFound(_(
"Instance is not in Rescue Mode: %s" % instance.name))
- original_vm = self._get_vm_opaque_ref(instance)
+ original_vm_ref = self._get_vm_opaque_ref(instance)
vbds = self._session.get_xenapi().VM.get_VBDs(rescue_vm)
instance._rescue = False
@@ -662,20 +659,20 @@ class VMOps(object):
task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm)
self._session.wait_for_task(task2, instance.id)
- self._release_bootlock(original_vm)
- self._start(instance, original_vm)
+ self._release_bootlock(original_vm_ref)
+ self._start(instance, original_vm_ref)
def get_info(self, instance):
"""Return data about VM instance"""
- vm = self._get_vm_opaque_ref(instance)
- rec = self._session.get_xenapi().VM.get_record(vm)
- return VMHelper.compile_info(rec)
+ vm_ref = self._get_vm_opaque_ref(instance)
+ vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
+ return VMHelper.compile_info(vm_rec)
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)
- return VMHelper.compile_diagnostics(self._session, rec)
+ vm_ref = self._get_vm_opaque_ref(instance)
+ vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
+ return VMHelper.compile_diagnostics(self._session, vm_rec)
def get_console_output(self, instance):
"""Return snapshot of console"""
@@ -698,9 +695,9 @@ class VMOps(object):
# at this stage even though they aren't implemented because these will
# be needed for multi-nic and there was no sense writing it for single
# network/single IP and then having to turn around and re-write it
- vm_opaque_ref = self._get_vm_opaque_ref(instance.id)
+ vm_ref = self._get_vm_opaque_ref(instance.id)
logging.debug(_("injecting network info to xenstore for vm: |%s|"),
- vm_opaque_ref)
+ vm_ref)
admin_context = context.get_admin_context()
IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id'])
networks = db.network_get_all_by_instance(admin_context,
@@ -731,11 +728,10 @@ class VMOps(object):
'ips': [ip_dict(ip) for ip in network_IPs],
'ip6s': [ip6_dict(ip) for ip in network_IPs]}
- self.write_to_param_xenstore(vm_opaque_ref, {location: mapping})
+ self.write_to_param_xenstore(vm_ref, {location: mapping})
try:
- self.write_to_xenstore(vm_opaque_ref, location,
- mapping['location'])
+ self.write_to_xenstore(vm_ref, location, mapping['location'])
except KeyError:
# catch KeyError for domid if instance isn't running
pass
@@ -747,8 +743,8 @@ class VMOps(object):
Creates vifs for an instance
"""
- vm_opaque_ref = self._get_vm_opaque_ref(instance.id)
- logging.debug(_("creating vif(s) for vm: |%s|"), vm_opaque_ref)
+ vm_ref = self._get_vm_opaque_ref(instance.id)
+ logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
if networks is None:
networks = db.network_get_all_by_instance(admin_context,
instance['id'])
@@ -768,12 +764,8 @@ class VMOps(object):
except AttributeError:
device = "0"
- VMHelper.create_vif(
- self._session,
- vm_opaque_ref,
- network_ref,
- instance.mac_address,
- device)
+ VMHelper.create_vif(self._session, vm_ref, network_ref,
+ instance.mac_address, device)
def reset_network(self, instance):
"""
@@ -837,9 +829,9 @@ class VMOps(object):
Any errors raised by the plugin will in turn raise a RuntimeError here.
"""
instance_id = vm.id
- vm = self._get_vm_opaque_ref(vm)
- rec = self._session.get_xenapi().VM.get_record(vm)
- args = {'dom_id': rec['domid'], 'path': path}
+ vm_ref = self._get_vm_opaque_ref(vm)
+ vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
+ args = {'dom_id': vm_rec['domid'], 'path': path}
args.update(addl_args)
try:
task = self._session.async_call_plugin(plugin, method, args)
@@ -919,9 +911,9 @@ class VMOps(object):
value for 'keys' is passed, the returned dict is filtered to only
return the values for those keys.
"""
- vm = self._get_vm_opaque_ref(instance_or_vm)
+ vm_ref = self._get_vm_opaque_ref(instance_or_vm)
data = self._session.call_xenapi_request('VM.get_xenstore_data',
- (vm, ))
+ (vm_ref, ))
ret = {}
if keys is None:
keys = data.keys()
@@ -939,11 +931,11 @@ class VMOps(object):
"""Takes a key/value pair and adds it to the xenstore parameter
record for the given vm instance. If the key exists in xenstore,
it is overwritten"""
- vm = self._get_vm_opaque_ref(instance_or_vm)
+ vm_ref = self._get_vm_opaque_ref(instance_or_vm)
self.remove_from_param_xenstore(instance_or_vm, key)
jsonval = json.dumps(val)
self._session.call_xenapi_request('VM.add_to_xenstore_data',
- (vm, key, jsonval))
+ (vm_ref, key, jsonval))
def write_to_param_xenstore(self, instance_or_vm, mapping):
"""Takes a dict and writes each key/value pair to the xenstore
@@ -958,14 +950,14 @@ class VMOps(object):
them from the xenstore parameter record data for the given VM.
If the key doesn't exist, the request is ignored.
"""
- vm = self._get_vm_opaque_ref(instance_or_vm)
+ vm_ref = self._get_vm_opaque_ref(instance_or_vm)
if isinstance(key_or_keys, basestring):
keys = [key_or_keys]
else:
keys = key_or_keys
for key in keys:
self._session.call_xenapi_request('VM.remove_from_xenstore_data',
- (vm, key))
+ (vm_ref, key))
def clear_param_xenstore(self, instance_or_vm):
"""Removes all data from the xenstore parameter record for this VM."""
--
cgit
From 25bbe2afb0be3c79264376dd6a11e2bc97847702 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Thu, 10 Mar 2011 11:17:34 -0800
Subject: fixed formatting and redundant imports
---
nova/api/ec2/cloud.py | 6 +++---
.../db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py | 1 -
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 6ed6186be..c6309f03c 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -701,9 +701,9 @@ class CloudController(object):
fixed = instance['fixed_ip']
floating_addr = fixed['floating_ips'][0]['address']
if instance['fixed_ip']['network'] and 'use_v6' in kwargs:
- i['dnsNameV6'] = utils.to_global_ipv6(
- instance['fixed_ip']['network']['cidr_v6'],
- instance['mac_address'])
+ i['dnsNameV6'] = utils.to_global_ipv6(
+ instance['fixed_ip']['network']['cidr_v6'],
+ instance['mac_address'])
i['privateDnsName'] = fixed_addr
i['publicDnsName'] = floating_addr
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
index d14f52af1..b8514c439 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/007_add_ipv6_flatmanager.py
@@ -12,7 +12,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
-from lib2to3.fixer_util import String
from sqlalchemy import *
from migrate import *
--
cgit
From 616723fe4e7d52b0b8ddafda10fcfe07a87609c8 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Thu, 10 Mar 2011 14:53:13 -0500
Subject: add docstring
---
nova/compute/api.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 2766ddc9c..efa051d10 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -81,6 +81,11 @@ class API(base.Base):
{"method": "get_network_topic", "args": {'fake': 1}})
def _check_personality_file_quota(self, context, personality_files):
+ """
+ Enforce quota limits on personality files
+
+ Raises a QuotaError if any limit is exceeded
+ """
limit = quota.allowed_personality_files(context)
if len(personality_files) > limit:
raise quota.QuotaError(_("Personality limit exceeded. You can "
--
cgit
From 03e5b8f7c4e1afc6637774acb3d28100035cd323 Mon Sep 17 00:00:00 2001
From: "Kevin L. Mitchell"
Date: Thu, 10 Mar 2011 20:04:21 +0000
Subject: Partial revert of one conversion due to phantom magic exception from
SQLAlchemy in unrelated code; convert all deletes
---
nova/db/sqlalchemy/api.py | 65 ++++++++++++++++++++++++++++++-----------------
1 file changed, 41 insertions(+), 24 deletions(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 2b60ab7d5..31adb33ee 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -701,11 +701,21 @@ def instance_data_get_for_project(context, project_id):
def instance_destroy(context, instance_id):
session = get_session()
with session.begin():
- session.query(models.Instance).\
- filter_by(id=instance_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': models.Instance.updated_at + 0})
+ session.execute('update instances set deleted=1,'
+ 'deleted_at=:at where id=:id',
+ {'id': instance_id,
+ 'at': datetime.datetime.utcnow()})
+ # NOTE(klmitch): for some reason, using the SQLAlchemy code
+ # here instead of the direct SQL update above causes the
+ # test_run_terminate_timestamps test (and only that one) to
+ # fail with an obscure TypeError exception from deep within
+ # SQLAlchemy; the nearest nova function in the traceback is
+ # instance_get()
+ # session.query(models.Instance).\
+ # filter_by(id=instance_id).\
+ # update({'deleted': 1,
+ # 'deleted_at': datetime.datetime.utcnow(),
+ # 'updated_at': models.Instance.updated_at + 0})
session.query(models.SecurityGroupInstanceAssociation).\
filter_by(instance_id=instance_id).\
update({'deleted': 1,
@@ -1837,12 +1847,15 @@ def user_create(_context, values):
def user_delete(context, id):
session = get_session()
with session.begin():
- session.execute('delete from user_project_association '
- 'where user_id=:id', {'id': id})
- session.execute('delete from user_role_association '
- 'where user_id=:id', {'id': id})
- session.execute('delete from user_project_role_association '
- 'where user_id=:id', {'id': id})
+ session.query(models.UserProjectAssociation).\
+ filter_by(user_id=id).\
+ delete()
+ session.query(models.UserRoleAssociation).\
+ filter_by(user_id=id).\
+ delete()
+ session.query(models.UserProjectRoleAssociation).\
+ filter_by(user_id=id).\
+ delete()
user_ref = user_get(context, id, session=session)
session.delete(user_ref)
@@ -1933,10 +1946,12 @@ def project_update(context, project_id, values):
def project_delete(context, id):
session = get_session()
with session.begin():
- session.execute('delete from user_project_association '
- 'where project_id=:id', {'id': id})
- session.execute('delete from user_project_role_association '
- 'where project_id=:id', {'id': id})
+ session.query(models.UserProjectAssociation).\
+ filter_by(project_id=id).\
+ delete()
+ session.query(models.UserProjectRoleAssociation).\
+ filter_by(project_id=id).\
+ delete()
project_ref = project_get(context, id, session=session)
session.delete(project_ref)
@@ -1961,11 +1976,11 @@ def user_get_roles_for_project(context, user_id, project_id):
def user_remove_project_role(context, user_id, project_id, role):
session = get_session()
with session.begin():
- session.execute('delete from user_project_role_association where '
- 'user_id=:user_id and project_id=:project_id and '
- 'role=:role', {'user_id': user_id,
- 'project_id': project_id,
- 'role': role})
+ session.query(models.UserProjectRoleAssociation).\
+ filter_by(user_id=user_id).\
+ filter_by(project_id=project_id).\
+ filter_by(role=role).\
+ delete()
def user_remove_role(context, user_id, role):
@@ -2116,8 +2131,9 @@ def console_delete(context, console_id):
session = get_session()
with session.begin():
# consoles are meant to be transient. (mdragon)
- session.execute('delete from consoles '
- 'where id=:id', {'id': console_id})
+ session.query(models.Console).\
+ filter_by(id=console_id).\
+ delete()
def console_get_by_pool_instance(context, pool_id, instance_id):
@@ -2273,8 +2289,9 @@ def zone_update(context, zone_id, values):
def zone_delete(context, zone_id):
session = get_session()
with session.begin():
- session.execute('delete from zones '
- 'where id=:id', {'id': zone_id})
+ session.query(models.Zone).\
+ filter_by(id=zone_id).\
+ delete()
@require_admin_context
--
cgit
From c177074649055f1da2ca97eb3c07139571d4a664 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 10 Mar 2011 12:10:49 -0800
Subject: Implements basic OpenStack API client, ready to support API tests
---
nova/tests/integrated/__init__.py | 20 +++
nova/tests/integrated/api/__init__.py | 20 +++
nova/tests/integrated/api/client.py | 224 ++++++++++++++++++++++++++++++++++
3 files changed, 264 insertions(+)
create mode 100644 nova/tests/integrated/__init__.py
create mode 100644 nova/tests/integrated/api/__init__.py
create mode 100644 nova/tests/integrated/api/client.py
diff --git a/nova/tests/integrated/__init__.py b/nova/tests/integrated/__init__.py
new file mode 100644
index 000000000..10e0a91d7
--- /dev/null
+++ b/nova/tests/integrated/__init__.py
@@ -0,0 +1,20 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Justin Santa Barbara
+#
+# 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.
+
+"""
+:mod:`integrated` -- Tests whole systems, using mock services where needed
+=================================
+"""
diff --git a/nova/tests/integrated/api/__init__.py b/nova/tests/integrated/api/__init__.py
new file mode 100644
index 000000000..5798ab3d1
--- /dev/null
+++ b/nova/tests/integrated/api/__init__.py
@@ -0,0 +1,20 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Justin Santa Barbara
+#
+# 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.
+
+"""
+:mod:`api` -- OpenStack API client, for testing rather than production
+=================================
+"""
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
new file mode 100644
index 000000000..5ab247fab
--- /dev/null
+++ b/nova/tests/integrated/api/client.py
@@ -0,0 +1,224 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Justin Santa Barbara
+#
+# 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 json
+import httplib
+import urlparse
+
+from nova import log as logging
+
+
+LOG = logging.getLogger('nova.tests.api')
+
+
+class OpenstackApiException(Exception):
+ def __init__(self, message=None, response=None):
+ self.response = response
+ if not message:
+ message = 'Unspecified error'
+
+ if response:
+ _status = response.status
+ _body = response.read()
+
+ message = _('%(message)s\nStatus Code: %(_status)s\n'
+ 'Body: %(_body)s') % locals()
+
+ super(OpenstackApiException, self).__init__(message)
+
+
+class OpenstackApiAuthenticationException(OpenstackApiException):
+ def __init__(self, response=None, message=None):
+ if not message:
+ message = _("Authentication error")
+ super(OpenstackApiAuthenticationException, self).__init__(message,
+ response)
+
+
+class OpenstackApiNotFoundException(OpenstackApiException):
+ def __init__(self, response=None, message=None):
+ if not message:
+ message = _("Item not found")
+ super(OpenstackApiNotFoundException, self).__init__(message, response)
+
+
+class TestOpenStackClient(object):
+ """ A really basic OpenStack API client that is under our control,
+ so we can make changes / insert hooks for testing"""
+
+ def __init__(self, auth_user, auth_key, auth_uri):
+ super(TestOpenStackClient, self).__init__()
+ self.auth_result = None
+ self.auth_user = auth_user
+ self.auth_key = auth_key
+ self.auth_uri = auth_uri
+
+ def request(self, url, method='GET', body=None, headers=None):
+ if headers is None:
+ headers = {}
+
+ parsed_url = urlparse.urlparse(url)
+ port = parsed_url.port
+ hostname = parsed_url.hostname
+ scheme = parsed_url.scheme
+
+ if scheme == 'http':
+ conn = httplib.HTTPConnection(hostname,
+ port=port)
+ elif scheme == 'https':
+ conn = httplib.HTTPSConnection(hostname,
+ port=port)
+ else:
+ raise OpenstackApiException("Unknown scheme: %s" % url)
+
+ relative_url = parsed_url.path
+ if parsed_url.query:
+ relative_url = relative_url + parsed_url.query
+ LOG.info(_("Doing %(method)s on %(relative_url)s") % locals())
+ if body:
+ LOG.info(_("Body: %s") % body)
+
+ conn.request(method, relative_url, body, headers)
+ response = conn.getresponse()
+ return response
+
+ def _authenticate(self):
+ if self.auth_result:
+ return self.auth_result
+
+ headers = {'X-Auth-User': self.auth_user,
+ 'X-Auth-Key': self.auth_key}
+ response = self.request(self.auth_uri,
+ headers=headers)
+ if not response.status in [204]:
+ raise OpenstackApiAuthenticationException(response=response)
+
+ auth_headers = {}
+ for k, v in response.getheaders():
+ auth_headers[k] = v
+
+ self.auth_result = auth_headers
+ return self.auth_result
+
+ def api_request(self, relative_uri, check_response_status=None, **kwargs):
+ auth_result = self._authenticate()
+
+ base_uri = auth_result['X-Server-Management-Url']
+ full_uri = base_uri + relative_uri
+
+ headers = kwargs.setdefault('headers', {})
+ headers['X-Auth-Token'] = auth_result['X-Auth-Token']
+
+ LOG.debug(_("HTTP request on %s") % (relative_uri))
+
+ response = self.request(full_uri, **kwargs)
+
+ LOG.debug(_("Response => code %s") % (response.status))
+
+ if check_response_status:
+ if not response.status in check_response_status:
+ if response.status == 404:
+ raise OpenstackApiNotFoundException(response=response)
+ else:
+ raise OpenstackApiException(
+ message=_("Unexpected status code"),
+ response=response)
+
+ return response
+
+ def _decode_json(self, response):
+ body = response.read()
+ LOG.debug(_("Decoding JSON: %s") % (body))
+ return json.loads(body)
+
+ def api_get(self, relative_uri, **kwargs):
+ kwargs.setdefault('check_response_status', [200])
+ response = self.api_request(relative_uri, **kwargs)
+ return self._decode_json(response)
+
+ def api_post(self, relative_uri, body, **kwargs):
+ kwargs['method'] = 'POST'
+ if body:
+ headers = kwargs.setdefault('headers', {})
+ headers['Content-Type'] = 'application/json'
+ kwargs['body'] = json.dumps(body)
+
+ kwargs.setdefault('check_response_status', [200])
+ response = self.api_request(relative_uri, **kwargs)
+ return self._decode_json(response)
+
+ def api_delete(self, relative_uri, **kwargs):
+ kwargs['method'] = 'DELETE'
+ kwargs.setdefault('check_response_status', [200, 202])
+ return self.api_request(relative_uri, **kwargs)
+
+ def get_keys_detail(self):
+ return self.api_get('/keys/detail')['keys']
+
+ def post_key(self, key):
+ return self.api_post('/keys', key)['key']
+
+ def delete_key(self, key_id):
+ return self.api_delete('/keys/%s' % key_id)
+
+ def get_volume(self, volume_id):
+ return self.api_get('/volumes/%s' % volume_id)['volume']
+
+ def get_volumes_detail(self):
+ return self.api_get('/volumes/detail')['volumes']
+
+ def post_volume(self, volume):
+ return self.api_post('/volumes', volume)['volume']
+
+ def delete_volume(self, volume_id):
+ return self.api_delete('/volumes/%s' % volume_id)
+
+ def get_server(self, server_id):
+ return self.api_get('/servers/%s' % server_id)['server']
+
+ def get_servers(self, detail=True):
+ rel_url = '/servers/detail' if detail else '/servers'
+ return self.api_get(rel_url)['servers']
+
+ def post_server(self, server):
+ return self.api_post('/servers', server)['server']
+
+ def delete_server(self, server_id):
+ return self.api_delete('/servers/%s' % server_id)
+
+ def get_image(self, image_id):
+ return self.api_get('/images/%s' % image_id)['image']
+
+ def get_images_detail(self):
+ return self.api_get('/images/detail')['images']
+
+ def post_image(self, image):
+ return self.api_post('/images', image)['image']
+
+ def delete_image(self, image_id):
+ return self.api_delete('/images/%s' % image_id)
+
+ def get_flavor(self, flavor_id):
+ return self.api_get('/flavors/%s' % flavor_id)['flavor']
+
+ def get_flavors_detail(self):
+ return self.api_get('/flavors/detail')['flavors']
+
+ def post_flavor(self, flavor):
+ return self.api_post('/flavors', flavor)['flavor']
+
+ def delete_flavor(self, flavor_id):
+ return self.api_delete('/flavors/%s' % flavor_id)
--
cgit
From 8aabc32a69bf47075a3fd8e677d1bd70cbbca339 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 10 Mar 2011 21:13:07 +0100
Subject: Add basic test case.
---
nova/tests/test_cloud.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py
index cf8ee7eff..db7c15aeb 100644
--- a/nova/tests/test_cloud.py
+++ b/nova/tests/test_cloud.py
@@ -353,6 +353,18 @@ class CloudTestCase(test.TestCase):
self.assertEqual('', img.metadata['description'])
shutil.rmtree(pathdir)
+ def test_metadata_works_without_kernel_and_ramdisk(self):
+ inst = db.instance_create(self.context, {'host': self.compute.host,
+ 'vcpus': 2,
+ 'image_id': '123456',
+ 'user_data': '' })
+ fixed = self.network.allocate_fixed_ip(self.context, inst['id'])
+ try:
+ self.cloud.get_metadata(fixed)
+ finally:
+ self.network.deallocate_fixed_ip(self.context, fixed)
+ db.instance_destroy(self.context, inst['id'])
+
def test_update_of_instance_display_fields(self):
inst = db.instance_create(self.context, {})
ec2_id = ec2utils.id_to_ec2_id(inst['id'])
--
cgit
From bd06f0ac0d0d3e3c9d7b296c5fe4bb8a0dd44c89 Mon Sep 17 00:00:00 2001
From: "Kevin L. Mitchell"
Date: Thu, 10 Mar 2011 20:36:36 +0000
Subject: Last un-magiced session.execute() replaced with SQLAlchemy code...
---
nova/db/sqlalchemy/api.py | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 31adb33ee..88125aaf5 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -579,16 +579,17 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time):
session = get_session()
# NOTE(vish): The nested select is because sqlite doesn't support
# JOINs in UPDATEs.
- result = session.execute('UPDATE fixed_ips SET instance_id = NULL, '
- 'leased = 0 '
- 'WHERE network_id IN (SELECT id FROM networks '
- 'WHERE host = :host) '
- 'AND updated_at < :time '
- 'AND instance_id IS NOT NULL '
- 'AND allocated = 0',
- {'host': host,
- 'time': time})
- return result.rowcount
+ inner_q = session.query(models.Network.id).\
+ filter_by(host=host).\
+ subquery()
+ result = session.query(models.FixedIp).\
+ filter(models.FixedIp.network_id.in_(inner_q)).\
+ filter(models.FixedIp.updated_at < time).\
+ filter(models.FixedIp.instance_id != None).\
+ filter_by(allocated=0).\
+ update({'instance_id': None,
+ 'leased': 0})
+ return result
@require_admin_context
--
cgit
From 6b4beef8093b4a7b4d42818567b5afb023af9251 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 10 Mar 2011 12:55:06 -0800
Subject: Don't wrap keys and volumes till they're in the API
---
nova/tests/integrated/api/client.py | 21 ---------------------
1 file changed, 21 deletions(-)
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index 5ab247fab..8ec46b5ae 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -165,27 +165,6 @@ class TestOpenStackClient(object):
kwargs.setdefault('check_response_status', [200, 202])
return self.api_request(relative_uri, **kwargs)
- def get_keys_detail(self):
- return self.api_get('/keys/detail')['keys']
-
- def post_key(self, key):
- return self.api_post('/keys', key)['key']
-
- def delete_key(self, key_id):
- return self.api_delete('/keys/%s' % key_id)
-
- def get_volume(self, volume_id):
- return self.api_get('/volumes/%s' % volume_id)['volume']
-
- def get_volumes_detail(self):
- return self.api_get('/volumes/detail')['volumes']
-
- def post_volume(self, volume):
- return self.api_post('/volumes', volume)['volume']
-
- def delete_volume(self, volume_id):
- return self.api_delete('/volumes/%s' % volume_id)
-
def get_server(self, server_id):
return self.api_get('/servers/%s' % server_id)['server']
--
cgit
From f81d925f86670e3ed32d815c219824f627d82bc2 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 10 Mar 2011 13:51:26 -0800
Subject: Better logging, be more careful about when we throw login errors re
bug732866
---
nova/tests/integrated/api/client.py | 37 +++++++++++++++++++++++--------------
1 file changed, 23 insertions(+), 14 deletions(-)
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index 8ec46b5ae..d424a6428 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -99,11 +99,18 @@ class TestOpenStackClient(object):
if self.auth_result:
return self.auth_result
+ auth_uri = self.auth_uri
headers = {'X-Auth-User': self.auth_user,
'X-Auth-Key': self.auth_key}
- response = self.request(self.auth_uri,
+ response = self.request(auth_uri,
headers=headers)
- if not response.status in [204]:
+
+ http_status = response.status
+ LOG.debug(_("%(auth_uri)s => code %(http_status)s") % locals())
+
+ # Until bug732866 is fixed, we can't check this properly...
+ #if http_status == 401:
+ if http_status != 204:
raise OpenstackApiAuthenticationException(response=response)
auth_headers = {}
@@ -116,21 +123,21 @@ class TestOpenStackClient(object):
def api_request(self, relative_uri, check_response_status=None, **kwargs):
auth_result = self._authenticate()
- base_uri = auth_result['X-Server-Management-Url']
+ #NOTE(justinsb): httplib 'helpfully' converts headers to lower case
+ base_uri = auth_result['x-server-management-url']
full_uri = base_uri + relative_uri
headers = kwargs.setdefault('headers', {})
- headers['X-Auth-Token'] = auth_result['X-Auth-Token']
-
- LOG.debug(_("HTTP request on %s") % (relative_uri))
+ headers['X-Auth-Token'] = auth_result['x-auth-token']
response = self.request(full_uri, **kwargs)
-
- LOG.debug(_("Response => code %s") % (response.status))
+
+ http_status = response.status
+ LOG.debug(_("%(relative_uri)s => code %(http_status)s") % locals())
if check_response_status:
- if not response.status in check_response_status:
- if response.status == 404:
+ if not http_status in check_response_status:
+ if http_status == 404:
raise OpenstackApiNotFoundException(response=response)
else:
raise OpenstackApiException(
@@ -181,8 +188,9 @@ class TestOpenStackClient(object):
def get_image(self, image_id):
return self.api_get('/images/%s' % image_id)['image']
- def get_images_detail(self):
- return self.api_get('/images/detail')['images']
+ def get_images(self, detail=True):
+ rel_url = '/images/detail' if detail else '/images'
+ return self.api_get(rel_url)['images']
def post_image(self, image):
return self.api_post('/images', image)['image']
@@ -193,8 +201,9 @@ class TestOpenStackClient(object):
def get_flavor(self, flavor_id):
return self.api_get('/flavors/%s' % flavor_id)['flavor']
- def get_flavors_detail(self):
- return self.api_get('/flavors/detail')['flavors']
+ def get_flavors(self, detail=True):
+ rel_url = '/flavors/detail' if detail else '/flavors'
+ return self.api_get(rel_url)['flavors']
def post_flavor(self, flavor):
return self.api_post('/flavors', flavor)['flavor']
--
cgit
From 998975651ac2f2df7a3f8af16d62d197f451180f Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 10 Mar 2011 13:53:27 -0800
Subject: Test login. Uncovered bug732866
---
nova/tests/integrated/integrated_helpers.py | 182 ++++++++++++++++++++++++++++
nova/tests/integrated/test_login.py | 77 ++++++++++++
2 files changed, 259 insertions(+)
create mode 100644 nova/tests/integrated/integrated_helpers.py
create mode 100644 nova/tests/integrated/test_login.py
diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py
new file mode 100644
index 000000000..691ead6e1
--- /dev/null
+++ b/nova/tests/integrated/integrated_helpers.py
@@ -0,0 +1,182 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Justin Santa Barbara
+# 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.
+
+"""
+Provides common functionality for integrated unit tests
+"""
+
+import random
+import string
+
+from nova import exception
+from nova import flags
+from nova import service
+from nova import test # For the flags
+from nova.auth import manager
+from nova.exception import Error
+from nova.log import logging
+from nova.tests.integrated.api import client
+
+
+FLAGS = flags.FLAGS
+
+LOG = logging.getLogger('nova.tests.integrated')
+
+
+def generate_random_alphanumeric(length):
+ """Creates a random alphanumeric string of specified length"""
+ return ''.join(random.choice(string.ascii_uppercase + string.digits)
+ for _x in range(length))
+
+
+def generate_random_numeric(length):
+ """Creates a random numeric string of specified length"""
+ return ''.join(random.choice(string.digits)
+ for _x in range(length))
+
+
+def generate_new_element(items, prefix, numeric=False):
+ """Creates a random string with prefix, that is not in 'items' list"""
+ while True:
+ if numeric:
+ candidate = prefix + generate_random_numeric(8)
+ else:
+ candidate = prefix + generate_random_alphanumeric(8)
+ if not candidate in items:
+ return candidate
+ print "Random collision on %s" % candidate
+
+
+class TestUser(object):
+ def __init__(self, name, secret, auth_url):
+ self.name = name
+ self.secret = secret
+ self.auth_url = auth_url
+
+ if not auth_url:
+ raise exception.Error("auth_url is required")
+ self.openstack_api = client.TestOpenStackClient(self.name,
+ self.secret,
+ self.auth_url)
+
+
+class IntegratedUnitTestContext(object):
+ __INSTANCE = None
+
+ def __init__(self):
+ self.auth_manager = manager.AuthManager()
+
+ self.wsgi_server = None
+ self.wsgi_apps = []
+ self.api_service = None
+
+ self.services = []
+ self.auth_url = None
+ self.project_name = None
+
+ self.setup()
+
+ def setup(self):
+ self._start_services()
+
+ self._create_test_user()
+
+ def _create_test_user(self):
+ self.test_user = self._create_unittest_user()
+
+ # No way to currently pass this through the OpenStack API
+ self.project_name = 'openstack'
+ self._configure_project(self.project_name, self.test_user)
+
+ def _start_services(self):
+ # WSGI shutdown broken :-(
+ if not self.api_service:
+ self._start_api_service()
+
+ def cleanup(self):
+ for service in self.services:
+ service.kill()
+ self.services = []
+ # TODO(justinsb): Shutdown WSGI & anything else we startup
+ # WSGI shutdown broken :-(
+ # self.wsgi_server.terminate()
+ # self.wsgi_server = None
+ self.test_user = None
+
+ def _create_unittest_user(self):
+ users = self.auth_manager.get_users()
+ user_names = [user.name for user in users]
+ auth_name = generate_new_element(user_names, 'unittest_user_')
+ auth_key = generate_random_alphanumeric(16)
+
+ # Right now there's a bug where auth_name and auth_key are reversed
+ auth_key = auth_name
+
+ self.auth_manager.create_user(auth_name, auth_name, auth_key, False)
+ return TestUser(auth_name, auth_key, self.auth_url)
+
+ def _configure_project(self, project_name, user):
+ projects = self.auth_manager.get_projects()
+ project_names = [project.name for project in projects]
+ if not project_name in project_names:
+ project = self.auth_manager.create_project(project_name,
+ user.name,
+ description=None,
+ member_users=None)
+ else:
+ self.auth_manager.add_to_project(user.name, project_name)
+
+ def _start_api_service(self):
+ api_service = service.ApiService.create()
+ api_service.start()
+
+ if not api_service:
+ raise Exception("API Service was None")
+
+ # WSGI shutdown broken :-(
+ #self.services.append(volume_service)
+ self.api_service = api_service
+
+ self.auth_url = 'http://localhost:8774/v1.0'
+
+ return api_service
+
+ # WSGI shutdown broken :-(
+ #@staticmethod
+ #def get():
+ # if not IntegratedUnitTestContext.__INSTANCE:
+ # IntegratedUnitTestContext.startup()
+ # #raise Error("Must call IntegratedUnitTestContext::startup")
+ # return IntegratedUnitTestContext.__INSTANCE
+
+ @staticmethod
+ def startup():
+ # Because WSGI shutdown is broken at the moment, we have to recycle
+ if IntegratedUnitTestContext.__INSTANCE:
+ #raise Error("Multiple calls to IntegratedUnitTestContext.startup")
+ IntegratedUnitTestContext.__INSTANCE.setup()
+ else:
+ IntegratedUnitTestContext.__INSTANCE = IntegratedUnitTestContext()
+ return IntegratedUnitTestContext.__INSTANCE
+
+ @staticmethod
+ def shutdown():
+ if not IntegratedUnitTestContext.__INSTANCE:
+ raise Error("Must call IntegratedUnitTestContext::startup")
+ IntegratedUnitTestContext.__INSTANCE.cleanup()
+ # WSGI shutdown broken :-(
+ #IntegratedUnitTestContext.__INSTANCE = None
diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py
new file mode 100644
index 000000000..990dcaaf4
--- /dev/null
+++ b/nova/tests/integrated/test_login.py
@@ -0,0 +1,77 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Justin Santa Barbara
+# 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 unittest
+
+from nova import flags
+from nova.log import logging
+from nova.tests.integrated import integrated_helpers
+from nova.tests.integrated.api import client
+
+LOG = logging.getLogger('nova.tests.integrated')
+
+FLAGS = flags.FLAGS
+FLAGS.verbose = True
+
+
+class LoginTest(unittest.TestCase):
+ def setUp(self):
+ super(LoginTest, self).setUp()
+ context = integrated_helpers.IntegratedUnitTestContext.startup()
+ self.user = context.test_user
+ self.api = self.user.openstack_api
+
+ def tearDown(self):
+ integrated_helpers.IntegratedUnitTestContext.shutdown()
+ super(LoginTest, self).tearDown()
+
+ def test_login(self):
+ """Simple check - we list flavors - so we know we're logged in"""
+ flavors = self.api.get_flavors()
+ for flavor in flavors:
+ LOG.debug(_("flavor: %s") % flavor)
+
+ def test_bad_login_password(self):
+ """Test that I get a 401 with a bad username"""
+ bad_credentials_api = client.TestOpenStackClient(self.user.name,
+ "notso_password",
+ self.user.auth_url)
+
+ self.assertRaises(client.OpenstackApiAuthenticationException,
+ bad_credentials_api.get_flavors)
+
+ def test_bad_login_username(self):
+ """Test that I get a 401 with a bad password"""
+ bad_credentials_api = client.TestOpenStackClient("notso_username",
+ self.user.secret,
+ self.user.auth_url)
+
+ self.assertRaises(client.OpenstackApiAuthenticationException,
+ bad_credentials_api.get_flavors)
+
+
+ def test_bad_login_both_bad(self):
+ """Test that I get a 401 with both bad username and bad password"""
+ bad_credentials_api = client.TestOpenStackClient("notso_username",
+ "notso_password",
+ self.user.auth_url)
+
+ self.assertRaises(client.OpenstackApiAuthenticationException,
+ bad_credentials_api.get_flavors)
+
+if __name__ == "__main__":
+ unittest.main()
--
cgit
From f110e718807ea4747a0ff95138c488961257aa7f Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 10 Mar 2011 13:56:24 -0800
Subject: pep8 fun
---
nova/tests/integrated/api/client.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index d424a6428..da8d87e07 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -104,7 +104,7 @@ class TestOpenStackClient(object):
'X-Auth-Key': self.auth_key}
response = self.request(auth_uri,
headers=headers)
-
+
http_status = response.status
LOG.debug(_("%(auth_uri)s => code %(http_status)s") % locals())
@@ -131,7 +131,7 @@ class TestOpenStackClient(object):
headers['X-Auth-Token'] = auth_result['x-auth-token']
response = self.request(full_uri, **kwargs)
-
+
http_status = response.status
LOG.debug(_("%(relative_uri)s => code %(http_status)s") % locals())
--
cgit
From 3d6430ecd114daa21c72c3d215daaa94f0e87e62 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 10 Mar 2011 14:12:41 -0800
Subject: Re-removed the code that was deleted upstream but somehow didn't get
merged in. Bizarre!
---
nova/tests/integrated/api/client.py | 21 ---------------------
1 file changed, 21 deletions(-)
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index 0ce480ae7..da8d87e07 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -172,27 +172,6 @@ class TestOpenStackClient(object):
kwargs.setdefault('check_response_status', [200, 202])
return self.api_request(relative_uri, **kwargs)
- def get_keys_detail(self):
- return self.api_get('/keys/detail')['keys']
-
- def post_key(self, key):
- return self.api_post('/keys', key)['key']
-
- def delete_key(self, key_id):
- return self.api_delete('/keys/%s' % key_id)
-
- def get_volume(self, volume_id):
- return self.api_get('/volumes/%s' % volume_id)['volume']
-
- def get_volumes_detail(self):
- return self.api_get('/volumes/detail')['volumes']
-
- def post_volume(self, volume):
- return self.api_post('/volumes', volume)['volume']
-
- def delete_volume(self, volume_id):
- return self.api_delete('/volumes/%s' % volume_id)
-
def get_server(self, server_id):
return self.api_get('/servers/%s' % server_id)['server']
--
cgit
From 29bc4f5074ca3ada98a25a745077b998b4c5509c Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 10 Mar 2011 14:14:01 -0800
Subject: Pep8 / Style
---
nova/tests/integrated/test_login.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py
index 990dcaaf4..e362f92d6 100644
--- a/nova/tests/integrated/test_login.py
+++ b/nova/tests/integrated/test_login.py
@@ -22,6 +22,7 @@ from nova.log import logging
from nova.tests.integrated import integrated_helpers
from nova.tests.integrated.api import client
+
LOG = logging.getLogger('nova.tests.integrated')
FLAGS = flags.FLAGS
@@ -63,7 +64,6 @@ class LoginTest(unittest.TestCase):
self.assertRaises(client.OpenstackApiAuthenticationException,
bad_credentials_api.get_flavors)
-
def test_bad_login_both_bad(self):
"""Test that I get a 401 with both bad username and bad password"""
bad_credentials_api = client.TestOpenStackClient("notso_username",
--
cgit
From be66b329d5b94ffbfb782355ef342eadbaed72a5 Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Thu, 10 Mar 2011 22:14:53 +0000
Subject: Fix a fer nits jaypipes found in review.
---
nova/api/openstack/accounts.py | 18 +++++++++++++++---
nova/api/openstack/users.py | 4 ++--
nova/tests/api/openstack/test_accounts.py | 6 +++---
nova/tests/api/openstack/test_auth.py | 15 ++++-----------
nova/tests/api/openstack/test_users.py | 2 +-
5 files changed, 25 insertions(+), 20 deletions(-)
diff --git a/nova/api/openstack/accounts.py b/nova/api/openstack/accounts.py
index 3b90d2776..dd88c3390 100644
--- a/nova/api/openstack/accounts.py
+++ b/nova/api/openstack/accounts.py
@@ -21,6 +21,7 @@ from nova import log as logging
from nova import wsgi
from nova.auth import manager
+from nova.api.openstack import faults
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack')
@@ -44,11 +45,17 @@ class Controller(wsgi.Controller):
self.manager = manager.AuthManager()
def _check_admin(self, context):
- """ We cannot depend on the db layer to check for admin access
- for the auth manager, so we do it here """
+ """We cannot depend on the db layer to check for admin access
+ for the auth manager, so we do it here"""
if not context.is_admin:
raise exception.NotAuthorized(_("Not admin user."))
+ def index(self, req):
+ raise faults.Fault(exc.HTTPNotImplemented())
+
+ def detail(self, req):
+ raise faults.Fault(exc.HTTPNotImplemented())
+
def show(self, req, id):
"""Return data about the given account id"""
account = self.manager.get_project(id)
@@ -59,8 +66,13 @@ class Controller(wsgi.Controller):
self.manager.delete_project(id)
return {}
+ def create(self, req):
+ """We use update with create-or-update semantics
+ because the id comes from an external source"""
+ raise faults.Fault(exc.HTTPNotImplemented())
+
def update(self, req, id):
- """ This is really create or update. """
+ """This is really create or update."""
self._check_admin(req.environ['nova.context'])
env = self._deserialize(req.body, req)
description = env['account'].get('description')
diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py
index 83ebec964..5bb20a718 100644
--- a/nova/api/openstack/users.py
+++ b/nova/api/openstack/users.py
@@ -45,8 +45,8 @@ class Controller(wsgi.Controller):
self.manager = manager.AuthManager()
def _check_admin(self, context):
- """ We cannot depend on the db layer to check for admin access
- for the auth manager, so we do it here """
+ """We cannot depend on the db layer to check for admin access
+ for the auth manager, so we do it here"""
if not context.is_admin:
raise exception.NotAuthorized(_("Not admin user"))
diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py
index 746f02f57..78fceb47c 100644
--- a/nova/tests/api/openstack/test_accounts.py
+++ b/nova/tests/api/openstack/test_accounts.py
@@ -14,9 +14,10 @@
# under the License.
+import json
+
import stubout
import webob
-import json
import nova.api
import nova.api.openstack.auth
@@ -47,8 +48,7 @@ class AccountsTest(test.TestCase):
fake_init)
self.stubs.Set(nova.api.openstack.accounts.Controller, '_check_admin',
fake_admin_check)
- fakes.FakeAuthManager.auth_data = {}
- fakes.FakeAuthManager.projects = {}
+ fakes.FakeAuthManager.clear_fakes()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index 49f90879d..437a79ec5 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -51,9 +51,7 @@ class Test(test.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'herp', None, None, None)
- f.add_user('derp', u)
- f.create_project('test', u)
+ f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'herp'
@@ -67,9 +65,7 @@ class Test(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'herp', None, None, None)
- f.add_user('derp', u)
- f.create_project('test', u)
+ f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'herp'
@@ -86,7 +82,7 @@ class Test(test.TestCase):
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouter',
fakes.FakeRouter)
- req = webob.Request.blank('/v1.0/test/fake')
+ req = webob.Request.blank('/v1.0/fake')
req.headers['X-Auth-Token'] = token
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '200 OK')
@@ -180,9 +176,6 @@ class TestLimiter(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'herp', None, None, None)
- f.add_user('derp', u)
- f.create_project('test', u)
f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
req = webob.Request.blank('/v1.0/')
@@ -194,7 +187,7 @@ class TestLimiter(test.TestCase):
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouter',
fakes.FakeRouter)
- req = webob.Request.blank('/v1.0/test/fake')
+ req = webob.Request.blank'/v1.0/fake')
req.method = 'POST'
req.headers['X-Auth-Token'] = token
result = req.get_response(fakes.wsgi_app())
diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py
index 14c7897f0..1edefe713 100644
--- a/nova/tests/api/openstack/test_users.py
+++ b/nova/tests/api/openstack/test_users.py
@@ -13,10 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
+import json
import stubout
import webob
-import json
import nova.api
import nova.api.openstack.auth
--
cgit
From f251ef70bf83eebce0f851f8a1b052174be1d615 Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Thu, 10 Mar 2011 22:20:51 +0000
Subject: fix minor typo
---
nova/tests/api/openstack/test_auth.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index 437a79ec5..ff8d42a14 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -187,7 +187,7 @@ class TestLimiter(test.TestCase):
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouter',
fakes.FakeRouter)
- req = webob.Request.blank'/v1.0/fake')
+ req = webob.Request.blank('/v1.0/fake')
req.method = 'POST'
req.headers['X-Auth-Token'] = token
result = req.get_response(fakes.wsgi_app())
--
cgit
From 0d3e950ed4b0c8abbd619d4ac8724b4c3ce45bf1 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 10 Mar 2011 14:21:36 -0800
Subject: Document known bug numbers by the code which is degraded until the
bugs are fixed
---
nova/tests/integrated/api/client.py | 1 +
nova/tests/integrated/integrated_helpers.py | 6 ++++++
2 files changed, 7 insertions(+)
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index da8d87e07..6fba2930a 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -109,6 +109,7 @@ class TestOpenStackClient(object):
LOG.debug(_("%(auth_uri)s => code %(http_status)s") % locals())
# Until bug732866 is fixed, we can't check this properly...
+ # bug732866
#if http_status == 401:
if http_status != 204:
raise OpenstackApiAuthenticationException(response=response)
diff --git a/nova/tests/integrated/integrated_helpers.py b/nova/tests/integrated/integrated_helpers.py
index 691ead6e1..47093636e 100644
--- a/nova/tests/integrated/integrated_helpers.py
+++ b/nova/tests/integrated/integrated_helpers.py
@@ -104,6 +104,7 @@ class IntegratedUnitTestContext(object):
def _start_services(self):
# WSGI shutdown broken :-(
+ # bug731668
if not self.api_service:
self._start_api_service()
@@ -112,6 +113,7 @@ class IntegratedUnitTestContext(object):
service.kill()
self.services = []
# TODO(justinsb): Shutdown WSGI & anything else we startup
+ # bug731668
# WSGI shutdown broken :-(
# self.wsgi_server.terminate()
# self.wsgi_server = None
@@ -124,6 +126,7 @@ class IntegratedUnitTestContext(object):
auth_key = generate_random_alphanumeric(16)
# Right now there's a bug where auth_name and auth_key are reversed
+ # bug732907
auth_key = auth_name
self.auth_manager.create_user(auth_name, auth_name, auth_key, False)
@@ -156,6 +159,7 @@ class IntegratedUnitTestContext(object):
return api_service
# WSGI shutdown broken :-(
+ # bug731668
#@staticmethod
#def get():
# if not IntegratedUnitTestContext.__INSTANCE:
@@ -166,6 +170,7 @@ class IntegratedUnitTestContext(object):
@staticmethod
def startup():
# Because WSGI shutdown is broken at the moment, we have to recycle
+ # bug731668
if IntegratedUnitTestContext.__INSTANCE:
#raise Error("Multiple calls to IntegratedUnitTestContext.startup")
IntegratedUnitTestContext.__INSTANCE.setup()
@@ -179,4 +184,5 @@ class IntegratedUnitTestContext(object):
raise Error("Must call IntegratedUnitTestContext::startup")
IntegratedUnitTestContext.__INSTANCE.cleanup()
# WSGI shutdown broken :-(
+ # bug731668
#IntegratedUnitTestContext.__INSTANCE = None
--
cgit
From 7e95a65ccec2336176f389d614a85c9e70da374d Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Thu, 10 Mar 2011 22:33:45 +0000
Subject: re-added a test change I removed thinking it was related to removed
code. It wasn't :>
---
nova/tests/api/openstack/test_auth.py | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index ff8d42a14..aaaa4e415 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -65,7 +65,9 @@ class Test(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
+ u = nova.auth.manager.User(1, 'herp', None, None, None)
+ f.add_user('derp', u)
+ f.create_project('test', u)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'herp'
@@ -176,7 +178,9 @@ class TestLimiter(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
+ u = nova.auth.manager.User(1, 'herp', None, None, None)
+ f.add_user('derp', u)
+ f.create_project('test', u)
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'herp'
--
cgit
From c967679fa8144af57d79d89666ee29a0241d38a9 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Thu, 10 Mar 2011 17:36:41 -0500
Subject: switch to a more consistent usage of onset_files variable names
---
nova/api/openstack/servers.py | 59 ++++++++++++++++++++-----------
nova/compute/api.py | 38 ++++++++------------
nova/compute/manager.py | 2 +-
nova/quota.py | 30 ++++++++--------
nova/tests/api/openstack/test_servers.py | 48 ++++++++++++-------------
nova/tests/test_quota.py | 60 ++++++++++++++++----------------
6 files changed, 124 insertions(+), 113 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 7bef8eb32..adb5c5f99 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -169,20 +169,23 @@ class Controller(wsgi.Controller):
metadata.append({'key': k, 'value': v})
personality = env['server'].get('personality', [])
- personality_files = self._get_personality_files(personality)
-
- instances = self.compute_api.create(
- context,
- instance_types.get_by_flavor_id(env['server']['flavorId']),
- image_id,
- kernel_id=kernel_id,
- ramdisk_id=ramdisk_id,
- display_name=env['server']['name'],
- display_description=env['server']['name'],
- key_name=key_pair['name'],
- key_data=key_pair['public_key'],
- metadata=metadata,
- personality_files=personality_files)
+ onset_files = self._get_onset_files(personality)
+
+ try:
+ instances = self.compute_api.create(
+ context,
+ instance_types.get_by_flavor_id(env['server']['flavorId']),
+ image_id,
+ kernel_id=kernel_id,
+ ramdisk_id=ramdisk_id,
+ display_name=env['server']['name'],
+ display_description=env['server']['name'],
+ key_name=key_pair['name'],
+ key_data=key_pair['public_key'],
+ metadata=metadata,
+ onset_files=onset_files)
+ except QuotaError as error:
+ self._handle_quota_error(error)
server = _translate_keys(instances[0])
password = "%s%s" % (server['server']['name'][:4],
@@ -204,15 +207,15 @@ class Controller(wsgi.Controller):
else:
return self._deserialize(request.body, request.get_content_type())
- def _get_personality_files(self, personality):
+ def _get_onset_files(self, personality):
"""
- Create a list of personality files from the personality attribute
+ Create a list of onset files from the personality attribute
- At this time, personality_files must be formatted as a list of
+ At this time, onset_files must be formatted as a list of
(file_path, file_content) pairs for compatibility with the
underlying compute service.
"""
- personality_files = []
+ onset_files = []
for item in personality:
try:
path = item['path']
@@ -227,8 +230,24 @@ class Controller(wsgi.Controller):
except TypeError:
msg = 'Personality content for %s cannot be decoded' % path
raise exc.HTTPBadRequest(explanation=msg)
- personality_files.append((path, contents))
- return personality_files
+ onset_files.append((path, contents))
+ return onset_files
+
+ def _handle_quota_errors(self, error):
+ """
+ Reraise quota errors as api-specific http exceptions
+ """
+ if error.code == "OnsetFileLimitExceeded":
+ expl = "Personality file limit exceeded"
+ raise exc.HTTPBadRequest(explanation=expl)
+ if error.code == "OnsetFilePathLimitExceeded":
+ expl = "Personality file path too long"
+ raise exc.HTTPBadRequest(explanation=expl)
+ if error.code == "OnsetFileContentLimitExceeded":
+ expl = "Personality file content too long"
+ raise exc.HTTPBadRequest(explanation=expl)
+ # if the original error is okay, just reraise it
+ raise error
def update(self, req, id):
""" Updates the server name or password """
diff --git a/nova/compute/api.py b/nova/compute/api.py
index b97cadf61..140bbb3aa 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -80,30 +80,23 @@ class API(base.Base):
topic,
{"method": "get_network_topic", "args": {'fake': 1}})
- def _check_personality_file_quota(self, context, personality_files):
+ def _check_onset_file_quota(self, context, onset_files):
"""
- Enforce quota limits on personality files
+ Enforce quota limits on onset files
Raises a QuotaError if any limit is exceeded
"""
- limit = quota.allowed_personality_files(context)
- if len(personality_files) > limit:
- raise quota.QuotaError(_("Personality limit exceeded. You can "
- "only have %d personalities when "
- "creating an instance.") % limit,
- "PersonalityLimitExceeded")
- path_limit = quota.allowed_personality_path_bytes(context)
- content_limit = quota.allowed_personality_content_bytes(context)
- for path, content in personality_files:
+ limit = quota.allowed_onset_files(context)
+ if len(onset_files) > limit:
+ raise quota.QuotaError(code="OnsetFileLimitExceeded")
+ path_limit = quota.allowed_onset_file_path_bytes(context)
+ content_limit = quota.allowed_onset_file_content_bytes(context)
+ for path, content in onset_files:
if len(path) > path_limit:
- raise quota.QuotaError(
- _("Personality file path limit exceeded."),
- "PersonalityLimitExceeded")
+ raise quota.QuotaError(code="OnsetFilePathLimitExceeded")
if len(content) > content_limit:
- raise quota.QuotaError(
- _("Personality file content limit exceeded."),
- "PersonalityLimitExceeded")
- return personality_files
+ raise quota.QuotaError(code="OnsetFileContentLimitExceeded")
+ return onset_files
def create(self, context, instance_type,
image_id, kernel_id=None, ramdisk_id=None,
@@ -111,7 +104,7 @@ class API(base.Base):
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata=[],
- personality_files=None):
+ onset_files=None):
"""Create the number of instances requested if quota and
other arguments check out ok."""
@@ -149,9 +142,8 @@ class API(base.Base):
LOG.warn(msg)
raise quota.QuotaError(msg, "MetadataLimitExceeded")
- if personality_files is not None:
- personality_files = \
- self._check_personality_file_quota(context, personality_files)
+ if onset_files is not None:
+ onset_files = self._check_onset_file_quota(context, onset_files)
image = self.image_service.show(context, image_id)
if kernel_id is None:
@@ -248,7 +240,7 @@ class API(base.Base):
"args": {"topic": FLAGS.compute_topic,
"instance_id": instance_id,
"availability_zone": availability_zone,
- "personality_files": personality_files}})
+ "onset_files": onset_files}})
for group_id in security_groups:
self.trigger_security_group_members_refresh(elevated, group_id)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index d87290aae..601bb3084 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -173,7 +173,7 @@ class ComputeManager(manager.Manager):
"""Launch a new instance with specified options."""
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
- instance_ref.onset_files = kwargs.get('personality_files', [])
+ instance_ref.onset_files = kwargs.get('onset_files', [])
if instance_ref['name'] in self.driver.list_instances():
raise exception.Error(_("Instance has already been created"))
LOG.audit(_("instance %s: starting..."), instance_id,
diff --git a/nova/quota.py b/nova/quota.py
index fdef42bc5..e0fb97542 100644
--- a/nova/quota.py
+++ b/nova/quota.py
@@ -37,12 +37,12 @@ flags.DEFINE_integer('quota_floating_ips', 10,
'number of floating ips allowed per project')
flags.DEFINE_integer('quota_metadata_items', 128,
'number of metadata items allowed per instance')
-flags.DEFINE_integer('quota_personality_max_files', 5,
- 'number of personality files allowed')
-flags.DEFINE_integer('quota_personality_max_content_bytes', 10 * 1024,
- 'number of bytes allowed per personality file')
-flags.DEFINE_integer('quota_personality_max_path_bytes', 255,
- 'number of bytes allowed per personality file path')
+flags.DEFINE_integer('quota_max_onset_files', 5,
+ 'number of onset files allowed')
+flags.DEFINE_integer('quota_max_onset_file_content_bytes', 10 * 1024,
+ 'number of bytes allowed per onset file')
+flags.DEFINE_integer('quota_max_onset_file_path_bytes', 255,
+ 'number of bytes allowed per onset file path')
def get_quota(context, project_id):
@@ -113,19 +113,19 @@ def allowed_metadata_items(context, num_metadata_items):
return min(num_metadata_items, num_allowed_metadata_items)
-def allowed_personality_files(context):
- """Return the number of personality files allowed"""
- return int(FLAGS.quota_personality_max_files)
+def allowed_onset_files(context):
+ """Return the number of onset files allowed"""
+ return int(FLAGS.quota_max_onset_files)
-def allowed_personality_content_bytes(context):
- """Return the number of bytes allowed per personality content"""
- return int(FLAGS.quota_personality_max_content_bytes)
+def allowed_onset_file_content_bytes(context):
+ """Return the number of bytes allowed per onset file content"""
+ return int(FLAGS.quota_max_onset_file_content_bytes)
-def allowed_personality_path_bytes(context):
- """Return the number of bytes allowed in a personality file path"""
- return int(FLAGS.quota_personality_max_path_bytes)
+def allowed_onset_file_path_bytes(context):
+ """Return the number of bytes allowed in an onset file path"""
+ return int(FLAGS.quota_max_onset_file_path_bytes)
class QuotaError(exception.ApiError):
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 0561ad499..b6d88d833 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -833,13 +833,13 @@ class TestServerInstanceCreation(test.TestCase):
class MockComputeAPI(object):
def __init__(self):
- self.personality_files = None
+ self.onset_files = None
def create(self, *args, **kwargs):
- if 'personality_files' in kwargs:
- self.personality_files = kwargs['personality_files']
+ if 'onset_files' in kwargs:
+ self.onset_files = kwargs['onset_files']
else:
- self.personality_files = None
+ self.onset_files = None
return [{'id': '1234', 'display_name': 'fakeinstance'}]
def set_admin_password(self, *args, **kwargs):
@@ -919,46 +919,46 @@ class TestServerInstanceCreation(test.TestCase):
request = self._get_create_request_json(body_dict)
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
- return request, response, compute_api.personality_files
+ return request, response, compute_api.onset_files
def _create_instance_with_personality_xml(self, personality):
body_dict = self._create_personality_request_dict(personality)
request = self._get_create_request_xml(body_dict)
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
- return request, response, compute_api.personality_files
+ return request, response, compute_api.onset_files
def test_create_instance_with_no_personality(self):
- request, response, personality_files = \
+ request, response, onset_files = \
self._create_instance_with_personality_json(personality=None)
self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [])
+ self.assertEquals(onset_files, [])
def test_create_instance_with_no_personality_xml(self):
- request, response, personality_files = \
+ request, response, onset_files = \
self._create_instance_with_personality_xml(personality=None)
self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [])
+ self.assertEquals(onset_files, [])
def test_create_instance_with_personality(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Hello, World!"\n'
b64contents = base64.b64encode(contents)
personality = [(path, b64contents)]
- request, response, personality_files = \
+ request, response, onset_files = \
self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [(path, contents)])
+ self.assertEquals(onset_files, [(path, contents)])
def test_create_instance_with_personality_xml(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Hello, World!"\n'
b64contents = base64.b64encode(contents)
personality = [(path, b64contents)]
- request, response, personality_files = \
+ request, response, onset_files = \
self._create_instance_with_personality_xml(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [(path, contents)])
+ self.assertEquals(onset_files, [(path, contents)])
def test_create_instance_with_personality_no_path(self):
personality = [('/remove/this/path',
@@ -969,7 +969,7 @@ class TestServerInstanceCreation(test.TestCase):
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.personality_files, None)
+ self.assertEquals(compute_api.onset_files, None)
def _test_create_instance_with_personality_no_path_xml(self):
personality = [('/remove/this/path',
@@ -980,7 +980,7 @@ class TestServerInstanceCreation(test.TestCase):
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.personality_files, None)
+ self.assertEquals(compute_api.onset_files, None)
def test_create_instance_with_personality_no_contents(self):
personality = [('/test/path',
@@ -991,7 +991,7 @@ class TestServerInstanceCreation(test.TestCase):
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.personality_files, None)
+ self.assertEquals(compute_api.onset_files, None)
def test_create_instance_with_personality_not_a_list(self):
personality = [('/test/path', base64.b64encode('test\ncontents\n'))]
@@ -1002,16 +1002,16 @@ class TestServerInstanceCreation(test.TestCase):
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.personality_files, None)
+ self.assertEquals(compute_api.onset_files, None)
def test_create_instance_with_personality_with_non_b64_content(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Oh no!"\n'
personality = [(path, contents)]
- request, response, personality_files = \
+ request, response, onset_files = \
self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 400)
- self.assertEquals(personality_files, None)
+ self.assertEquals(onset_files, None)
def test_create_instance_with_three_personalities(self):
files = [
@@ -1022,19 +1022,19 @@ class TestServerInstanceCreation(test.TestCase):
personality = []
for path, content in files:
personality.append((path, base64.b64encode(content)))
- request, response, personality_files = \
+ request, response, onset_files = \
self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, files)
+ self.assertEquals(onset_files, files)
def test_create_instance_personality_empty_content(self):
path = '/my/file/path'
contents = ''
personality = [(path, contents)]
- request, response, personality_files = \
+ request, response, onset_files = \
self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(personality_files, [(path, contents)])
+ self.assertEquals(onset_files, [(path, contents)])
def test_create_instance_admin_pass_json(self):
request, response, dummy = \
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index 86b1d40fb..d94381aa2 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -200,66 +200,66 @@ class QuotaTestCase(test.TestCase):
image_id='fake',
metadata=metadata)
- def test_allowed_personality_files(self):
+ def test_allowed_onset_files(self):
self.assertEqual(
- quota.allowed_personality_files(self.context),
- FLAGS.quota_personality_max_files)
+ quota.allowed_onset_files(self.context),
+ FLAGS.quota_max_onset_files)
- def _create_with_personality(self, files):
+ def _create_with_onset_files(self, files):
api = compute.API(image_service=self.StubImageService())
api.create(self.context, min_count=1, max_count=1,
instance_type='m1.small', image_id='fake',
- personality_files=files)
+ onset_files=files)
- def test_no_personality_files(self):
+ def test_no_onset_files(self):
api = compute.API(image_service=self.StubImageService())
api.create(self.context, instance_type='m1.small', image_id='fake')
- def test_max_personality_files(self):
+ def test_max_onset_files(self):
files = []
- for i in xrange(FLAGS.quota_personality_max_files):
+ for i in xrange(FLAGS.quota_max_onset_files):
files.append(('/my/path%d' % i, 'config = test\n'))
- self._create_with_personality(files) # no QuotaError
+ self._create_with_onset_files(files) # no QuotaError
- def test_too_many_personality_files(self):
+ def test_too_many_onset_files(self):
files = []
- for i in xrange(FLAGS.quota_personality_max_files + 1):
+ for i in xrange(FLAGS.quota_max_onset_files + 1):
files.append(('/my/path%d' % i, 'my\ncontent%d\n' % i))
self.assertRaises(quota.QuotaError,
- self._create_with_personality, files)
+ self._create_with_onset_files, files)
- def test_allowed_personality_content_bytes(self):
+ def test_allowed_onset_file_content_bytes(self):
self.assertEqual(
- quota.allowed_personality_content_bytes(self.context),
- FLAGS.quota_personality_max_content_bytes)
+ quota.allowed_onset_file_content_bytes(self.context),
+ FLAGS.quota_max_onset_file_content_bytes)
- def test_max_personality_content_bytes(self):
- max = FLAGS.quota_personality_max_content_bytes
+ def test_max_onset_file_content_bytes(self):
+ max = FLAGS.quota_max_onset_file_content_bytes
content = ''.join(['a' for i in xrange(max)])
files = [('/test/path', content)]
- self._create_with_personality(files) # no QuotaError
+ self._create_with_onset_files(files) # no QuotaError
- def test_too_many_personality_content_bytes(self):
- max = FLAGS.quota_personality_max_content_bytes
+ def test_too_many_onset_file_content_bytes(self):
+ max = FLAGS.quota_max_onset_file_content_bytes
content = ''.join(['a' for i in xrange(max + 1)])
files = [('/test/path', content)]
self.assertRaises(quota.QuotaError,
- self._create_with_personality, files)
+ self._create_with_onset_files, files)
- def test_allowed_personality_path_bytes(self):
+ def test_allowed_onset_file_path_bytes(self):
self.assertEqual(
- quota.allowed_personality_path_bytes(self.context),
- FLAGS.quota_personality_max_path_bytes)
+ quota.allowed_onset_file_path_bytes(self.context),
+ FLAGS.quota_max_onset_file_path_bytes)
- def test_max_personality_path_bytes(self):
- max = FLAGS.quota_personality_max_path_bytes
+ def test_max_onset_file_path_bytes(self):
+ max = FLAGS.quota_max_onset_file_path_bytes
path = ''.join(['a' for i in xrange(max)])
files = [(path, 'config = quotatest')]
- self._create_with_personality(files) # no QuotaError
+ self._create_with_onset_files(files) # no QuotaError
- def test_too_many_personality_path_bytes(self):
- max = FLAGS.quota_personality_max_path_bytes
+ def test_too_many_onset_file_path_bytes(self):
+ max = FLAGS.quota_max_onset_file_path_bytes
path = ''.join(['a' for i in xrange(max + 1)])
files = [(path, 'config = quotatest')]
self.assertRaises(quota.QuotaError,
- self._create_with_personality, files)
+ self._create_with_onset_files, files)
--
cgit
From 2379fc056d96d56c852e94fe7c3898049a3670bc Mon Sep 17 00:00:00 2001
From: Eric Windisch
Date: Thu, 10 Mar 2011 19:26:20 -0500
Subject: execvp: fix params
---
.../networking/etc/xensource/scripts/vif_rules.py | 33 +++++++++++-----------
1 file changed, 16 insertions(+), 17 deletions(-)
diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
index d2b2d61e6..93aed2986 100755
--- a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
+++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
@@ -71,13 +71,13 @@ def apply_iptables_rules(command, params):
iptables = lambda *rule: execute('/sbin/iptables', *rule)
iptables('-D', 'FORWARD', '-m', 'physdev',
- '--physdev-in', '%(VIF)s' % params,
- '-s', '%(IP)s' % params,
+ '--physdev-in', params['VIF'],
+ '-s', params['IP'],
'-j', 'ACCEPT')
if command == 'online':
iptables('-A', 'FORWARD', '-m', 'physdev',
- '--physdev-in', '%(VIF)s' % params,
- '-s', '%(IP)s' % params,
+ '--physdev-in', params['VIF'],
+ '-s', params['IP'],
'-j', 'ACCEPT')
@@ -85,25 +85,24 @@ def apply_arptables_rules(command, params):
arptables = lambda *rule: execute('/sbin/arptables', *rule)
arptables('-D', 'FORWARD', '--opcode', 'Request',
- '--in-interface', '%(VIF)s' % params,
- '--source-ip', '%(IP)s' % params,
- '--source-mac', '%(MAC)s' % params,
+ '--in-interface', params['VIF'],
+ '--source-ip', params['IP'],
+ '--source-mac', params['MAC'],
'-j', 'ACCEPT')
arptables('-D', 'FORWARD', '--opcode', 'Reply',
- '--in-interface', '%(VIF)s' % params,
- '--source-ip', '%(IP)s' % params,
- '--source-mac', '%(MAC)s' % params,
+ '--in-interface', params['VIF'],
+ '--source-ip', params['IP'],
+ '--source-mac', params['MAC'],
'-j', 'ACCEPT')
if command == 'online':
arptables('-A', 'FORWARD', '--opcode', 'Request',
- '--in-interface', '%(VIF)s' % params
- '--source-ip', '%(IP)s' % params,
- '--source-mac', '%(MAC)s' % params,
+ '--in-interface', params['VIF'],
+ '--source-mac', params['MAC'],
'-j', 'ACCEPT')
arptables('-A', 'FORWARD', '--opcode', 'Reply',
- '--in-interface', '%(VIF)s' % params,
- '--source-ip', '%(IP)s' % params,
- '--source-mac', '%(MAC)s' % params,
+ '--in-interface', params['VIF'],
+ '--source-ip', params['IP'],
+ '--source-mac', params['MAC'],
'-j', 'ACCEPT')
@@ -130,7 +129,7 @@ def apply_ebtables_rules(command, params):
'-i', params['VIF'], '-j', 'DROP')
if command == 'online':
ebtables('-I', 'FORWARD', '1', '-s', '!', params['MAC'],
- '-i', '%(VIF)s', '-j', 'DROP')
+ '-i', params['VIF'], '-j', 'DROP')
if __name__ == "__main__":
--
cgit
From c540d6d7fef1da68a42c16ac1f9a44337661bb0d Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Thu, 10 Mar 2011 20:12:58 -0500
Subject: Added version attribute to RequestContext class. Set the version in
the nova.context object at the middleware level. Prototyped how we can
serialize ip addresses based on the version.
---
nova/api/openstack/auth.py | 3 ++-
nova/api/openstack/servers.py | 38 ++++++++++++++++++++++++++------------
nova/context.py | 7 +++++--
3 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index de8905f46..320443935 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -57,7 +57,8 @@ class AuthMiddleware(wsgi.Middleware):
return faults.Fault(webob.exc.HTTPUnauthorized())
project = self.auth.get_project(FLAGS.default_project)
- req.environ['nova.context'] = context.RequestContext(user, project)
+ req.environ['nova.context'] = context.RequestContext(user, project,
+ version=req.script_name.replace('/v', ''))
return self.application
def has_authentication(self, req):
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index dc28a0782..ec542bc92 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -39,7 +39,7 @@ LOG = logging.getLogger('server')
FLAGS = flags.FLAGS
-def _translate_detail_keys(inst):
+def _translate_detail_keys(req, inst):
""" Coerces into dictionary format, mapping everything to Rackspace-like
attributes for return"""
power_mapping = {
@@ -54,6 +54,7 @@ def _translate_detail_keys(inst):
power_state.CRASHED: 'error',
power_state.FAILED: 'error'}
inst_dict = {}
+ version = req.environ['nova.context'].version
mapped_keys = dict(status='state', imageId='image_id',
flavorId='instance_type', name='display_name', id='id')
@@ -62,15 +63,7 @@ def _translate_detail_keys(inst):
inst_dict[k] = inst[v]
inst_dict['status'] = power_mapping[inst_dict['status']]
- inst_dict['addresses'] = dict(public=[], private=[])
-
- # grab single private fixed ip
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- inst_dict['addresses']['private'] = private_ips
-
- # grab all public floating ips
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- inst_dict['addresses']['public'] = public_ips
+ inst_dict['addresses'] = _addresses_generator(version)(inst)
# Return the metadata as a dictionary
metadata = {}
@@ -91,6 +84,27 @@ def _translate_keys(inst):
return dict(server=dict(id=inst['id'], name=inst['display_name']))
+def _addresses_generator(version):
+
+ def _gen_addresses_1_0(inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ return dict(public=public_ips, private=private_ips)
+
+ def _gen_addresses_1_1(inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ private_ips = [dict(version=4, addr=a) for a in private_ips]
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ public_ips = [dict(version=4, addr=a) for a in public_ips]
+ return dict(public=public_ips, private=private_ips)
+
+ dispatch_table = {
+ '1.0': _gen_addresses_1_0,
+ '1.1': _gen_addresses_1_1,
+ }
+
+ return dispatch_table[version]
+
class Controller(wsgi.Controller):
""" The Server API controller for the OpenStack API """
@@ -120,14 +134,14 @@ class Controller(wsgi.Controller):
"""
instance_list = self.compute_api.get_all(req.environ['nova.context'])
limited_list = common.limited(instance_list, req)
- res = [entity_maker(inst)['server'] for inst in limited_list]
+ res = [entity_maker(req, inst)['server'] for inst in limited_list]
return dict(servers=res)
def show(self, req, id):
""" Returns server details by server id """
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- return _translate_detail_keys(instance)
+ return _translate_detail_keys(req, instance)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
diff --git a/nova/context.py b/nova/context.py
index 0256bf448..677bd2e7e 100644
--- a/nova/context.py
+++ b/nova/context.py
@@ -29,7 +29,8 @@ from nova import utils
class RequestContext(object):
def __init__(self, user, project, is_admin=None, read_deleted=False,
- remote_address=None, timestamp=None, request_id=None):
+ remote_address=None, timestamp=None, request_id=None,
+ version='1.1'):
if hasattr(user, 'id'):
self._user = user
self.user_id = user.id
@@ -60,6 +61,7 @@ class RequestContext(object):
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-'
request_id = ''.join([random.choice(chars) for x in xrange(20)])
self.request_id = request_id
+ self.version = version
@property
def user(self):
@@ -93,7 +95,8 @@ class RequestContext(object):
'read_deleted': self.read_deleted,
'remote_address': self.remote_address,
'timestamp': utils.isotime(self.timestamp),
- 'request_id': self.request_id}
+ 'request_id': self.request_id,
+ 'version': self.version}
@classmethod
def from_dict(cls, values):
--
cgit
From 76b5871adbb1bfc2e3d2a1151a00fa654c45953d Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Thu, 10 Mar 2011 20:35:37 -0500
Subject: _translate_keys now needs one more argument, the request object.
---
nova/api/openstack/servers.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index ec542bc92..bd317f995 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -78,7 +78,7 @@ def _translate_detail_keys(req, inst):
return dict(server=inst_dict)
-def _translate_keys(inst):
+def _translate_keys(req, inst):
""" Coerces into dictionary format, excluding all model attributes
save for id and name """
return dict(server=dict(id=inst['id'], name=inst['display_name']))
@@ -193,7 +193,7 @@ class Controller(wsgi.Controller):
metadata=metadata,
onset_files=env.get('onset_files', []))
- server = _translate_keys(instances[0])
+ server = _translate_keys(req, instances[0])
password = "%s%s" % (server['server']['name'][:4],
utils.generate_password(12))
server['server']['adminPass'] = password
--
cgit
From b9a479ffc8e9db0c1888047d7f3df99b3b57b2ec Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: Thu, 10 Mar 2011 21:44:01 -0500
Subject: Make linux_net ensure_bridge commands that add and remove ip addr's
from devices/bridges work with with the latest utils.execute method (execvp).
---
nova/network/linux_net.py | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 9f9d282b6..e69ed2f75 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -513,11 +513,9 @@ def ensure_bridge(bridge, interface, net_attrs=None):
for line in out.split("\n"):
fields = line.split()
if fields and fields[0] == "inet":
- params = ' '.join(fields[1:-1])
- _execute('sudo', 'ip', 'addr',
- 'del', params, 'dev', fields[-1])
- _execute('sudo', 'ip', 'addr',
- 'add', params, 'dev', bridge)
+ params = fields[1:-1]
+ _execute(*_ip_bridge_cmd('del', params, fields[-1]))
+ _execute(*_ip_bridge_cmd('add', params, bridge))
if gateway:
_execute('sudo', 'route', 'add', '0.0.0.0', 'gw', gateway)
out, err = _execute('sudo', 'brctl', 'addif', bridge, interface,
@@ -739,3 +737,12 @@ def _ra_pid_for(bridge):
if os.path.exists(pid_file):
with open(pid_file, 'r') as f:
return int(f.read())
+
+
+def _ip_bridge_cmd(action, params, device):
+ """Build commands to add/del ips to bridges/devices"""
+
+ cmd = ['sudo', 'ip', 'addr', action]
+ cmd.extend(params)
+ cmd.extend(['dev', device])
+ return cmd
--
cgit
From 67a8d635af0a64ad220b163c00b96eadf7daf93f Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 11 Mar 2011 09:54:08 +0100
Subject: Make Authors check account for tests being run with different
os.getcwd() depending on how they're run. Add missing people to Authors.
---
.mailmap | 1 +
Authors | 1 +
nova/tests/test_misc.py | 12 +++++++-----
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/.mailmap b/.mailmap
index ed4404ad5..ccf2109a7 100644
--- a/.mailmap
+++ b/.mailmap
@@ -28,6 +28,7 @@
+
diff --git a/Authors b/Authors
index 7993955e2..4ee0643cf 100644
--- a/Authors
+++ b/Authors
@@ -19,6 +19,7 @@ Devin Carlen
Ed Leafe
Eldar Nugaev
Eric Day
+Eric Windisch
Ewan Mellor
Hisaharu Ishii
Hisaki Ohara
diff --git a/nova/tests/test_misc.py b/nova/tests/test_misc.py
index a658e4978..1fbaf304f 100644
--- a/nova/tests/test_misc.py
+++ b/nova/tests/test_misc.py
@@ -24,18 +24,19 @@ from nova.utils import parse_mailmap, str_dict_replace, synchronized
class ProjectTestCase(test.TestCase):
def test_authors_up_to_date(self):
- if os.path.exists('.bzr'):
+ topdir = os.path.normpath(os.path.dirname(__file__) + '/../../')
+ if os.path.exists(os.path.join(topdir, '.bzr')):
contributors = set()
- mailmap = parse_mailmap('.mailmap')
+ mailmap = parse_mailmap(os.path.join(topdir, '.mailmap'))
import bzrlib.workingtree
- tree = bzrlib.workingtree.WorkingTree.open('.')
+ tree = bzrlib.workingtree.WorkingTree.open(topdir)
tree.lock_read()
try:
parents = tree.get_parent_ids()
g = tree.branch.repository.get_graph()
- for p in parents[1:]:
+ for p in parents:
rev_ids = [r for r, _ in g.iter_ancestry(parents)
if r != "null:"]
revs = tree.branch.repository.get_revisions(rev_ids)
@@ -44,7 +45,8 @@ class ProjectTestCase(test.TestCase):
email = author.split(' ')[-1]
contributors.add(str_dict_replace(email, mailmap))
- authors_file = open('Authors', 'r').read()
+ authors_file = open(os.path.join(topdir, 'Authors'),
+ 'r').read()
missing = set()
for contributor in contributors:
--
cgit
From 46d1f6a8c888c1f6fdf12cf26df67eada1e8505b Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 11 Mar 2011 11:24:22 +0100
Subject: Use self.instances.pop in unfilter_instance to make the check/removal
atomic.
Move the semaphore grab outside the for loop in refresh_security_group_rules to avoid reading a value from self.instances, blocking waiting for the semaphore, having the instance be removed in the mean time, and then add its rules back.
---
nova/virt/libvirt_conn.py | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index b74ed25f9..d82b33ddd 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1238,13 +1238,12 @@ class IptablesFirewallDriver(FirewallDriver):
pass
def unfilter_instance(self, instance):
- if instance['id'] in self.instances:
- del self.instances[instance['id']]
+ if self.instances.pop(instance['id'], False):
self.remove_filters_for_instance(instance)
self.iptables.apply()
else:
LOG.info(_('Attempted to unfilter instance %s which is not '
- 'filtered'), instance['id'])
+ 'filtered'), instance['id'])
def prepare_instance_filter(self, instance):
self.instances[instance['id']] = instance
@@ -1387,11 +1386,11 @@ class IptablesFirewallDriver(FirewallDriver):
pass
def refresh_security_group_rules(self, security_group):
- for instance in self.instances.values():
- # We use the semaphore to make sure noone applies the rule set
- # after we've yanked the existing rules but before we've put in
- # the new ones.
- with self.iptables.semaphore:
+ # We use the semaphore to make sure noone applies the rule set
+ # after we've yanked the existing rules but before we've put in
+ # the new ones.
+ with self.iptables.semaphore:
+ for instance in self.instances.values():
self.remove_filters_for_instance(instance)
self.add_filters_for_instance(instance)
self.iptables.apply()
--
cgit
From d250d522b5d6c164435fc254223ff9a0f055cf05 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 11 Mar 2011 17:55:28 +0100
Subject: Remove broken test. At least this way, it'll actually fix the problem
and be mergable.
---
nova/tests/test_cloud.py | 12 ------------
1 file changed, 12 deletions(-)
diff --git a/nova/tests/test_cloud.py b/nova/tests/test_cloud.py
index db7c15aeb..cf8ee7eff 100644
--- a/nova/tests/test_cloud.py
+++ b/nova/tests/test_cloud.py
@@ -353,18 +353,6 @@ class CloudTestCase(test.TestCase):
self.assertEqual('', img.metadata['description'])
shutil.rmtree(pathdir)
- def test_metadata_works_without_kernel_and_ramdisk(self):
- inst = db.instance_create(self.context, {'host': self.compute.host,
- 'vcpus': 2,
- 'image_id': '123456',
- 'user_data': '' })
- fixed = self.network.allocate_fixed_ip(self.context, inst['id'])
- try:
- self.cloud.get_metadata(fixed)
- finally:
- self.network.deallocate_fixed_ip(self.context, fixed)
- db.instance_destroy(self.context, inst['id'])
-
def test_update_of_instance_display_fields(self):
inst = db.instance_create(self.context, {})
ec2_id = ec2utils.id_to_ec2_id(inst['id'])
--
cgit
From 65f6648f61cb6eeb5cd109fe08ef2ab2f3646c8b Mon Sep 17 00:00:00 2001
From: Eric Windisch
Date: Fri, 11 Mar 2011 12:09:20 -0500
Subject: cast execute commands to str
---
plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
index 93aed2986..48122e6d6 100755
--- a/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
+++ b/plugins/xenserver/networking/etc/xensource/scripts/vif_rules.py
@@ -54,6 +54,7 @@ def main(dom_id, command, only_this_vif=None):
def execute(*command, return_stdout=False):
devnull = open(os.devnull, 'w')
+ command = map(str, command)
proc = subprocess.Popen(command, close_fds=True,
stdout=subprocess.PIPE, stderr=devnull)
devnull.close()
--
cgit
From 36b5f7d9cf377ce2a4dcdad07e7e14062cd3ec4d Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 11 Mar 2011 11:22:23 -0600
Subject: Further vmops cleanup
---
nova/virt/xenapi/vmops.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 5375df5b4..f012fa446 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -536,10 +536,10 @@ class VMOps(object):
"""
instance_id = instance.id
LOG.info(_("Destroying VM for Instance %(instance_id)s") % locals())
- vm = VMHelper.lookup(self._session, instance.name)
- return self._destroy(instance, vm, shutdown=True)
+ vm_ref = VMHelper.lookup(self._session, instance.name)
+ return self._destroy(instance, vm_ref, shutdown=True)
- def _destroy(self, instance, vm, shutdown=True,
+ def _destroy(self, instance, vm_ref, shutdown=True,
destroy_kernel_ramdisk=True):
"""
Destroys VM instance by performing:
@@ -549,17 +549,17 @@ class VMOps(object):
3. Destroying kernel and ramdisk files (if necessary)
4. Destroying that actual VM record
"""
- if vm is None:
+ if vm_ref is None:
LOG.warning(_("VM is not present, skipping destroy..."))
return
if shutdown:
- self._shutdown(instance, vm)
+ self._shutdown(instance, vm_ref)
- self._destroy_vdis(instance, vm)
+ self._destroy_vdis(instance, vm_ref)
if destroy_kernel_ramdisk:
- self._destroy_kernel_ramdisk(instance, vm)
- self._destroy_vm(instance, vm)
+ self._destroy_kernel_ramdisk(instance, vm_ref)
+ self._destroy_vm(instance, vm_ref)
def _wait_with_callback(self, instance_id, task, callback):
ret = None
--
cgit
From cfc7d21b959bc929295868aeb3e84ea56afbfd9c Mon Sep 17 00:00:00 2001
From: "Kevin L. Mitchell"
Date: Fri, 11 Mar 2011 17:41:22 +0000
Subject: Discovered literal_column(), which does exactly what I need
---
nova/db/sqlalchemy/api.py | 49 +++++++++++++++--------------------------------
1 file changed, 15 insertions(+), 34 deletions(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 88125aaf5..431cf6e8e 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -34,6 +34,7 @@ from sqlalchemy.orm import joinedload
from sqlalchemy.orm import joinedload_all
from sqlalchemy.sql import exists
from sqlalchemy.sql import func
+from sqlalchemy.sql.expression import literal_column
FLAGS = flags.FLAGS
@@ -702,28 +703,16 @@ def instance_data_get_for_project(context, project_id):
def instance_destroy(context, instance_id):
session = get_session()
with session.begin():
- session.execute('update instances set deleted=1,'
- 'deleted_at=:at where id=:id',
- {'id': instance_id,
- 'at': datetime.datetime.utcnow()})
- # NOTE(klmitch): for some reason, using the SQLAlchemy code
- # here instead of the direct SQL update above causes the
- # test_run_terminate_timestamps test (and only that one) to
- # fail with an obscure TypeError exception from deep within
- # SQLAlchemy; the nearest nova function in the traceback is
- # instance_get()
- # session.query(models.Instance).\
- # filter_by(id=instance_id).\
- # update({'deleted': 1,
- # 'deleted_at': datetime.datetime.utcnow(),
- # 'updated_at': models.Instance.updated_at + 0})
+ session.query(models.Instance).\
+ filter_by(id=instance_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
session.query(models.SecurityGroupInstanceAssociation).\
filter_by(instance_id=instance_id).\
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
- 'updated_at':
- (models.SecurityGroupInstanceAssociation.
- updated_at + 0)})
+ 'updated_at': literal_column('updated_at')})
@require_context
@@ -969,7 +958,7 @@ def key_pair_destroy_all_by_user(context, user_id):
filter_by(user_id=user_id).\
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': models.KeyPair.updated_at + 0})
+ 'updated_at': literal_column('updated_at')})
@require_context
@@ -1082,7 +1071,7 @@ def network_disassociate_all(context):
session = get_session()
session.query(models.Network).\
update({'project_id': None,
- 'updated_at': models.Network.updated_at + 0})
+ 'updated_at': literal_column('updated_at')})
@require_context
@@ -1456,7 +1445,7 @@ def volume_destroy(context, volume_id):
filter_by(id=volume_id).\
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': models.Volume.updated_at + 0})
+ 'updated_at': literal_column('updated_at')})
session.query(models.ExportDevice).\
filter_by(volume_id=volume_id).\
update({'volume_id': None})
@@ -1686,22 +1675,17 @@ def security_group_destroy(context, security_group_id):
filter_by(id=security_group_id).\
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
- 'updated_at':
- models.SecurityGroup.updated_at + 0})
+ 'updated_at': literal_column('updated_at')})
session.query(models.SecurityGroupInstanceAssociation).\
filter_by(security_group_id=security_group_id).\
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
- 'updated_at':
- (models.SecurityGroupInstanceAssociation.
- updated_at + 0)})
+ 'updated_at': literal_column('updated_at')})
session.query(models.SecurityGroupIngressRule).\
filter_by(group_id=security_group_id).\
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
- 'updated_at':
- (models.SecurityGroupIngressRule.
- updated_at + 0)})
+ 'updated_at': literal_column('updated_at')})
@require_context
@@ -1712,14 +1696,11 @@ def security_group_destroy_all(context, session=None):
session.query(models.SecurityGroup).\
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
- 'updated_at':
- models.SecurityGroup.updated_at + 0})
+ 'updated_at': literal_column('updated_at')})
session.query(models.SecurityGroupIngressRule).\
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
- 'updated_at':
- (models.SecurityGroupIngressRule.
- updated_at + 0)})
+ 'updated_at': literal_column('updated_at')})
###################
--
cgit
From a5415e8fc40eaa82761532e5ba83c3800cf9ed78 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Fri, 11 Mar 2011 12:45:53 -0500
Subject: Need to set version to '1.0' in the nova.context in test code for
tests to be happy.
---
nova/api/openstack/auth.py | 3 ++-
nova/tests/api/openstack/fakes.py | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 320443935..8461a8059 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -57,8 +57,9 @@ class AuthMiddleware(wsgi.Middleware):
return faults.Fault(webob.exc.HTTPUnauthorized())
project = self.auth.get_project(FLAGS.default_project)
+ version = req.path.split('/')[1].replace('v', '')
req.environ['nova.context'] = context.RequestContext(user, project,
- version=req.script_name.replace('/v', ''))
+ version=version)
return self.application
def has_authentication(self, req):
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 2c4e57246..8ec1629f4 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -68,7 +68,7 @@ def fake_auth_init(self, application):
@webob.dec.wsgify
def fake_wsgi(self, req):
- req.environ['nova.context'] = context.RequestContext(1, 1)
+ req.environ['nova.context'] = context.RequestContext(1, 1, version='1.0')
if req.body:
req.environ['inst_dict'] = json.loads(req.body)
return self.application
--
cgit
From e891e48b63065a7218627289a908aece0f6a3730 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Fri, 11 Mar 2011 10:31:08 -0800
Subject: Made changes to xs-ipv6 code impacted because of addition of
flatmanger ipv6 support
---
nova/virt/xenapi/vmops.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 9ac83efb0..52d2652f9 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -617,9 +617,10 @@ class VMOps(object):
def ip6_dict(ip6):
return {
- "ip": ip6.addressV6,
- "netmask": ip6.netmaskV6,
- "gateway": ip6.gatewayV6,
+ "ip": utils.to_global_ipv6(network['cidr_v6'],
+ instance['mac_address']),
+ "netmask": network['netmask_v6'],
+ "gateway": network['gateway_v6'],
"enabled": "1"}
mac_id = instance.mac_address.replace(':', '')
--
cgit
From 195926d0635c0217edccf1cd763425163d3e92e7 Mon Sep 17 00:00:00 2001
From: "Kevin L. Mitchell"
Date: Fri, 11 Mar 2011 19:22:31 +0000
Subject: Minor stylistic updates affecting indentation
---
nova/db/sqlalchemy/api.py | 132 +++++++++++++++++++++++-----------------------
1 file changed, 66 insertions(+), 66 deletions(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 89745aa95..08bc8fe2f 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -581,15 +581,15 @@ def fixed_ip_disassociate_all_by_timeout(_context, host, time):
# NOTE(vish): The nested select is because sqlite doesn't support
# JOINs in UPDATEs.
inner_q = session.query(models.Network.id).\
- filter_by(host=host).\
- subquery()
+ filter_by(host=host).\
+ subquery()
result = session.query(models.FixedIp).\
- filter(models.FixedIp.network_id.in_(inner_q)).\
- filter(models.FixedIp.updated_at < time).\
- filter(models.FixedIp.instance_id != None).\
- filter_by(allocated=0).\
- update({'instance_id': None,
- 'leased': 0})
+ filter(models.FixedIp.network_id.in_(inner_q)).\
+ filter(models.FixedIp.updated_at < time).\
+ filter(models.FixedIp.instance_id != None).\
+ filter_by(allocated=0).\
+ update({'instance_id': None,
+ 'leased': 0})
return result
@@ -704,15 +704,15 @@ def instance_destroy(context, instance_id):
session = get_session()
with session.begin():
session.query(models.Instance).\
- filter_by(id=instance_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ filter_by(id=instance_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
session.query(models.SecurityGroupInstanceAssociation).\
- filter_by(instance_id=instance_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ filter_by(instance_id=instance_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
@require_context
@@ -955,10 +955,10 @@ def key_pair_destroy_all_by_user(context, user_id):
session = get_session()
with session.begin():
session.query(models.KeyPair).\
- filter_by(user_id=user_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ filter_by(user_id=user_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
@require_context
@@ -1079,8 +1079,8 @@ def network_disassociate(context, network_id):
def network_disassociate_all(context):
session = get_session()
session.query(models.Network).\
- update({'project_id': None,
- 'updated_at': literal_column('updated_at')})
+ update({'project_id': None,
+ 'updated_at': literal_column('updated_at')})
@require_context
@@ -1463,16 +1463,16 @@ def volume_destroy(context, volume_id):
session = get_session()
with session.begin():
session.query(models.Volume).\
- filter_by(id=volume_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ filter_by(id=volume_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
session.query(models.ExportDevice).\
- filter_by(volume_id=volume_id).\
- update({'volume_id': None})
+ filter_by(volume_id=volume_id).\
+ update({'volume_id': None})
session.query(models.IscsiTarget).\
- filter_by(volume_id=volume_id).\
- update({'volume_id': None})
+ filter_by(volume_id=volume_id).\
+ update({'volume_id': None})
@require_admin_context
@@ -1693,20 +1693,20 @@ def security_group_destroy(context, security_group_id):
session = get_session()
with session.begin():
session.query(models.SecurityGroup).\
- filter_by(id=security_group_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ filter_by(id=security_group_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
session.query(models.SecurityGroupInstanceAssociation).\
- filter_by(security_group_id=security_group_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ filter_by(security_group_id=security_group_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
session.query(models.SecurityGroupIngressRule).\
- filter_by(group_id=security_group_id).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ filter_by(group_id=security_group_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
@require_context
@@ -1715,13 +1715,13 @@ def security_group_destroy_all(context, session=None):
session = get_session()
with session.begin():
session.query(models.SecurityGroup).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
session.query(models.SecurityGroupIngressRule).\
- update({'deleted': 1,
- 'deleted_at': datetime.datetime.utcnow(),
- 'updated_at': literal_column('updated_at')})
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
###################
@@ -1851,14 +1851,14 @@ def user_delete(context, id):
session = get_session()
with session.begin():
session.query(models.UserProjectAssociation).\
- filter_by(user_id=id).\
- delete()
+ filter_by(user_id=id).\
+ delete()
session.query(models.UserRoleAssociation).\
- filter_by(user_id=id).\
- delete()
+ filter_by(user_id=id).\
+ delete()
session.query(models.UserProjectRoleAssociation).\
- filter_by(user_id=id).\
- delete()
+ filter_by(user_id=id).\
+ delete()
user_ref = user_get(context, id, session=session)
session.delete(user_ref)
@@ -1950,11 +1950,11 @@ def project_delete(context, id):
session = get_session()
with session.begin():
session.query(models.UserProjectAssociation).\
- filter_by(project_id=id).\
- delete()
+ filter_by(project_id=id).\
+ delete()
session.query(models.UserProjectRoleAssociation).\
- filter_by(project_id=id).\
- delete()
+ filter_by(project_id=id).\
+ delete()
project_ref = project_get(context, id, session=session)
session.delete(project_ref)
@@ -1980,10 +1980,10 @@ def user_remove_project_role(context, user_id, project_id, role):
session = get_session()
with session.begin():
session.query(models.UserProjectRoleAssociation).\
- filter_by(user_id=user_id).\
- filter_by(project_id=project_id).\
- filter_by(role=role).\
- delete()
+ filter_by(user_id=user_id).\
+ filter_by(project_id=project_id).\
+ filter_by(role=role).\
+ delete()
def user_remove_role(context, user_id, role):
@@ -2135,8 +2135,8 @@ def console_delete(context, console_id):
with session.begin():
# consoles are meant to be transient. (mdragon)
session.query(models.Console).\
- filter_by(id=console_id).\
- delete()
+ filter_by(id=console_id).\
+ delete()
def console_get_by_pool_instance(context, pool_id, instance_id):
@@ -2293,8 +2293,8 @@ def zone_delete(context, zone_id):
session = get_session()
with session.begin():
session.query(models.Zone).\
- filter_by(id=zone_id).\
- delete()
+ filter_by(id=zone_id).\
+ delete()
@require_admin_context
--
cgit
From 0a130010c26e4ef9ba5b9917ff47766de7805ab9 Mon Sep 17 00:00:00 2001
From: Eric Windisch
Date: Fri, 11 Mar 2011 14:24:03 -0500
Subject: process_input for tee. fixes: 733439
---
nova/virt/disk.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/disk.py b/nova/virt/disk.py
index 5d499c42c..9abe44cc3 100644
--- a/nova/virt/disk.py
+++ b/nova/virt/disk.py
@@ -189,4 +189,4 @@ def _inject_net_into_fs(net, fs):
utils.execute('sudo', 'chown', 'root:root', netdir)
utils.execute('sudo', 'chmod', 755, netdir)
netfile = os.path.join(netdir, 'interfaces')
- utils.execute('sudo', 'tee', netfile, net)
+ utils.execute('sudo', 'tee', netfile, process_input=net)
--
cgit
From d03b169e2343fc13f37324f0136835ae54f85569 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Fri, 11 Mar 2011 16:04:54 -0500
Subject: Added support for ips resource: /servers/1/ips Refactored
implmentation of how the servers response model is generated.
---
nova/api/openstack/servers.py | 85 +++++++++++++++++++++++++------------------
nova/context.py | 2 +-
2 files changed, 50 insertions(+), 37 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index bd317f995..b486dfebb 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -39,9 +39,41 @@ LOG = logging.getLogger('server')
FLAGS = flags.FLAGS
-def _translate_detail_keys(req, inst):
+def _translate_keys(req, inst):
+ """ Coerces into dictionary format, excluding all model attributes
+ save for id and name """
+ return dict(server=dict(id=inst['id'], name=inst['display_name']))
+
+
+def _build_addresses_10(inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ return dict(public=public_ips, private=private_ips)
+
+
+def _build_addresses_11(inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ private_ips = [dict(version=4, addr=a) for a in private_ips]
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ public_ips = [dict(version=4, addr=a) for a in public_ips]
+ return dict(public=public_ips, private=private_ips)
+
+
+def addresses_builder(req):
+ version = req.environ['nova.context'].version
+ if version == '1.1':
+ return _build_addresses_11
+ else:
+ return _build_addresses_10
+
+
+def build_server(req, inst, is_detail):
""" Coerces into dictionary format, mapping everything to Rackspace-like
attributes for return"""
+
+ if not is_detail:
+ return dict(server=dict(id=inst['id'], name=inst['display_name']))
+
power_mapping = {
None: 'build',
power_state.NOSTATE: 'build',
@@ -63,7 +95,7 @@ def _translate_detail_keys(req, inst):
inst_dict[k] = inst[v]
inst_dict['status'] = power_mapping[inst_dict['status']]
- inst_dict['addresses'] = _addresses_generator(version)(inst)
+ inst_dict['addresses'] = addresses_builder(req)(inst)
# Return the metadata as a dictionary
metadata = {}
@@ -78,33 +110,6 @@ def _translate_detail_keys(req, inst):
return dict(server=inst_dict)
-def _translate_keys(req, inst):
- """ Coerces into dictionary format, excluding all model attributes
- save for id and name """
- return dict(server=dict(id=inst['id'], name=inst['display_name']))
-
-
-def _addresses_generator(version):
-
- def _gen_addresses_1_0(inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- return dict(public=public_ips, private=private_ips)
-
- def _gen_addresses_1_1(inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- private_ips = [dict(version=4, addr=a) for a in private_ips]
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- public_ips = [dict(version=4, addr=a) for a in public_ips]
- return dict(public=public_ips, private=private_ips)
-
- dispatch_table = {
- '1.0': _gen_addresses_1_0,
- '1.1': _gen_addresses_1_1,
- }
-
- return dispatch_table[version]
-
class Controller(wsgi.Controller):
""" The Server API controller for the OpenStack API """
@@ -119,29 +124,37 @@ class Controller(wsgi.Controller):
self._image_service = utils.import_object(FLAGS.image_service)
super(Controller, self).__init__()
+ def ips(self, req, id):
+ try:
+ instance = self.compute_api.get(req.environ['nova.context'], id)
+ return addresses_builder(req)(instance)
+ except exception.NotFound:
+ return faults.Fault(exc.HTTPNotFound())
+
def index(self, req):
""" Returns a list of server names and ids for a given user """
- return self._items(req, entity_maker=_translate_keys)
+ return self._items(req, is_detail=False)
def detail(self, req):
""" Returns a list of server details for a given user """
- return self._items(req, entity_maker=_translate_detail_keys)
+ return self._items(req, is_detail=True)
- def _items(self, req, entity_maker):
+ def _items(self, req, is_detail):
"""Returns a list of servers for a given user.
- entity_maker - either _translate_detail_keys or _translate_keys
+ builder - the response model builder
"""
instance_list = self.compute_api.get_all(req.environ['nova.context'])
limited_list = common.limited(instance_list, req)
- res = [entity_maker(req, inst)['server'] for inst in limited_list]
+ res = [build_server(req, inst, is_detail)['server']
+ for inst in limited_list]
return dict(servers=res)
def show(self, req, id):
""" Returns server details by server id """
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- return _translate_detail_keys(req, instance)
+ return build_server(req, instance, is_detail=True)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -193,7 +206,7 @@ class Controller(wsgi.Controller):
metadata=metadata,
onset_files=env.get('onset_files', []))
- server = _translate_keys(req, instances[0])
+ server = build_server(req, instances[0], is_detail=False)
password = "%s%s" % (server['server']['name'][:4],
utils.generate_password(12))
server['server']['adminPass'] = password
diff --git a/nova/context.py b/nova/context.py
index 677bd2e7e..0f3eb9ae4 100644
--- a/nova/context.py
+++ b/nova/context.py
@@ -30,7 +30,7 @@ from nova import utils
class RequestContext(object):
def __init__(self, user, project, is_admin=None, read_deleted=False,
remote_address=None, timestamp=None, request_id=None,
- version='1.1'):
+ version=None):
if hasattr(user, 'id'):
self._user = user
self.user_id = user.id
--
cgit
From be9734b03bce871d32e21da2ba341dfa42aa020a Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Fri, 11 Mar 2011 17:19:14 -0500
Subject: Fixed lp732866 by catching relevant `exception.NotFound` exception.
Tests did not uncover this vulnerability due to "incorrect" FakeAuthManager.
I say "incorrect" because potentially different implementations (LDAP or
Database driven) of AuthManager might return different errors from
`get_user_from_access_key`.
Also, removed all references to 'bacon', 'ham', 'herp', and 'derp' and replaced them with hopefully more helpful terms.
---
nova/api/openstack/auth.py | 6 +++-
nova/tests/api/openstack/fakes.py | 5 +++-
nova/tests/api/openstack/test_auth.py | 56 +++++++++++++++++++++--------------
3 files changed, 43 insertions(+), 24 deletions(-)
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 4c6b58eff..f3a9bdeca 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -135,7 +135,11 @@ class AuthMiddleware(wsgi.Middleware):
req - wsgi.Request object
"""
ctxt = context.get_admin_context()
- user = self.auth.get_user_from_access_key(key)
+
+ try:
+ user = self.auth.get_user_from_access_key(key)
+ except exception.NotFound:
+ user = None
if user and user.name == username:
token_hash = hashlib.sha1('%s%s%f' % (username, key,
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index e50d11a3d..7cb974bb2 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -321,7 +321,10 @@ class FakeAuthManager(object):
(user.id == p.project_manager_id)]
def get_user_from_access_key(self, key):
- return FakeAuthManager.auth_data.get(key, None)
+ try:
+ return FakeAuthManager.auth_data[key]
+ except KeyError:
+ raise exc.NotFound
class FakeRateLimiter(object):
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index aaaa4e415..d1b3c0e10 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -51,11 +51,12 @@ class Test(test.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
- f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
+ f.add_user('user1_key',
+ nova.auth.manager.User(1, 'user1', None, None, None))
req = webob.Request.blank('/v1.0/')
- req.headers['X-Auth-User'] = 'herp'
- req.headers['X-Auth-Key'] = 'derp'
+ req.headers['X-Auth-User'] = 'user1'
+ req.headers['X-Auth-Key'] = 'user1_key'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '204 No Content')
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
@@ -65,13 +66,13 @@ class Test(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'herp', None, None, None)
- f.add_user('derp', u)
- f.create_project('test', u)
+ u = nova.auth.manager.User(1, 'user1', None, None, None)
+ f.add_user('user1_key', u)
+ f.create_project('user1_project', u)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
- req.headers['X-Auth-User'] = 'herp'
- req.headers['X-Auth-Key'] = 'derp'
+ req.headers['X-Auth-User'] = 'user1'
+ req.headers['X-Auth-Key'] = 'user1_key'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '204 No Content')
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
@@ -92,7 +93,7 @@ class Test(test.TestCase):
def test_token_expiry(self):
self.destroy_called = False
- token_hash = 'bacon'
+ token_hash = 'token_hash'
def destroy_token_mock(meh, context, token):
self.destroy_called = True
@@ -109,15 +110,26 @@ class Test(test.TestCase):
bad_token)
req = webob.Request.blank('/v1.0/')
- req.headers['X-Auth-Token'] = 'bacon'
+ req.headers['X-Auth-Token'] = 'token_hash'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '401 Unauthorized')
self.assertEqual(self.destroy_called, True)
- def test_bad_user(self):
+ def test_bad_user_bad_key(self):
+ req = webob.Request.blank('/v1.0/')
+ req.headers['X-Auth-User'] = 'unknown_user'
+ req.headers['X-Auth-Key'] = 'unknown_user_key'
+ result = req.get_response(fakes.wsgi_app())
+ self.assertEqual(result.status, '401 Unauthorized')
+
+ def test_bad_user_good_key(self):
+ f = fakes.FakeAuthManager()
+ u = nova.auth.manager.User(1, 'user1', None, None, None)
+ f.add_user('user1_key', u)
+
req = webob.Request.blank('/v1.0/')
- req.headers['X-Auth-User'] = 'herp'
- req.headers['X-Auth-Key'] = 'derp'
+ req.headers['X-Auth-User'] = 'unknown_user'
+ req.headers['X-Auth-Key'] = 'user1_key'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '401 Unauthorized')
@@ -128,7 +140,7 @@ class Test(test.TestCase):
def test_bad_token(self):
req = webob.Request.blank('/v1.0/')
- req.headers['X-Auth-Token'] = 'baconbaconbacon'
+ req.headers['X-Auth-Token'] = 'unknown_token'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '401 Unauthorized')
@@ -137,11 +149,11 @@ class TestFunctional(test.TestCase):
def test_token_expiry(self):
ctx = context.get_admin_context()
tok = db.auth_token_create(ctx, dict(
- token_hash='bacon',
+ token_hash='test_token_hash',
cdn_management_url='',
server_management_url='',
storage_url='',
- user_id='ham',
+ user_id='user1',
))
db.auth_token_update(ctx, tok.token_hash, dict(
@@ -149,13 +161,13 @@ class TestFunctional(test.TestCase):
))
req = webob.Request.blank('/v1.0/')
- req.headers['X-Auth-Token'] = 'bacon'
+ req.headers['X-Auth-Token'] = 'test_token_hash'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '401 Unauthorized')
def test_token_doesnotexist(self):
req = webob.Request.blank('/v1.0/')
- req.headers['X-Auth-Token'] = 'ham'
+ req.headers['X-Auth-Token'] = 'nonexistant_token_hash'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '401 Unauthorized')
@@ -178,13 +190,13 @@ class TestLimiter(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'herp', None, None, None)
- f.add_user('derp', u)
+ u = nova.auth.manager.User(1, 'user1', None, None, None)
+ f.add_user('user1_key', u)
f.create_project('test', u)
req = webob.Request.blank('/v1.0/')
- req.headers['X-Auth-User'] = 'herp'
- req.headers['X-Auth-Key'] = 'derp'
+ req.headers['X-Auth-User'] = 'user1'
+ req.headers['X-Auth-Key'] = 'user1_key'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
--
cgit
From 2ac7fa75c02c885fc9d4dfacba8318aadbdbfceb Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 11 Mar 2011 23:34:26 +0100
Subject: Indentation adjustment (cosmetical).
---
nova/virt/libvirt_conn.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index d82b33ddd..678331eed 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1243,7 +1243,7 @@ class IptablesFirewallDriver(FirewallDriver):
self.iptables.apply()
else:
LOG.info(_('Attempted to unfilter instance %s which is not '
- 'filtered'), instance['id'])
+ 'filtered'), instance['id'])
def prepare_instance_filter(self, instance):
self.instances[instance['id']] = instance
--
cgit
From a9d71273742f440af5687650dd9cd72d827a6bef Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 11 Mar 2011 23:36:28 +0100
Subject: Make the fallback value None instead of False
---
nova/virt/libvirt_conn.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 678331eed..d2061a0ca 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1238,7 +1238,7 @@ class IptablesFirewallDriver(FirewallDriver):
pass
def unfilter_instance(self, instance):
- if self.instances.pop(instance['id'], False):
+ if self.instances.pop(instance['id'], None):
self.remove_filters_for_instance(instance)
self.iptables.apply()
else:
--
cgit
From 5d0ca375e082c702b218c26f24d8009650a319a3 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Fri, 11 Mar 2011 14:36:31 -0800
Subject: Beginning of cleanup of FakeAuthManager
---
nova/tests/api/openstack/fakes.py | 41 +++++++++++++++++--------------
nova/tests/api/openstack/test_accounts.py | 4 +--
nova/tests/api/openstack/test_auth.py | 14 +++++------
nova/tests/api/openstack/test_users.py | 14 +++++------
4 files changed, 38 insertions(+), 35 deletions(-)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index e50d11a3d..52ac80e3f 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -228,12 +228,14 @@ class FakeAuthDatabase(object):
class FakeAuthManager(object):
- auth_data = {}
+ #NOTE(justinsb): Accessing static variables through instances is FUBAR
+ #NOTE(justinsb): This should also be private!
+ auth_data = []
projects = {}
@classmethod
def clear_fakes(cls):
- cls.auth_data = {}
+ cls.auth_data = []
cls.projects = {}
@classmethod
@@ -246,34 +248,40 @@ class FakeAuthManager(object):
'test',
[]))
- def add_user(self, key, user):
- FakeAuthManager.auth_data[key] = user
+ def add_user(self, user):
+ FakeAuthManager.auth_data.append(user)
def get_users(self):
- return FakeAuthManager.auth_data.values()
+ return FakeAuthManager.auth_data
def get_user(self, uid):
- for k, v in FakeAuthManager.auth_data.iteritems():
- if v.id == uid:
- return v
+ for u in FakeAuthManager.auth_data:
+ if u.id == uid:
+ return u
+ return None
+
+ def get_user_from_access_key(self, key):
+ for u in FakeAuthManager.auth_data:
+ if u.access == key:
+ return u
return None
def delete_user(self, uid):
- for k, v in FakeAuthManager.auth_data.items():
- if v.id == uid:
- del FakeAuthManager.auth_data[k]
+ for u in FakeAuthManager.auth_data:
+ if u.id == uid:
+ FakeAuthManager.auth_data.remove(u)
return None
def create_user(self, name, access=None, secret=None, admin=False):
u = User(name, name, access, secret, admin)
- FakeAuthManager.auth_data[access] = u
+ FakeAuthManager.auth_data.append(u)
return u
def modify_user(self, user_id, access=None, secret=None, admin=None):
user = None
- for k, v in FakeAuthManager.auth_data.iteritems():
- if v.id == user_id:
- user = v
+ for u in FakeAuthManager.auth_data:
+ if u.id == user_id:
+ user = u
if user:
user.access = access
user.secret = secret
@@ -320,9 +328,6 @@ class FakeAuthManager(object):
if (user.id in p.member_ids) or
(user.id == p.project_manager_id)]
- def get_user_from_access_key(self, key):
- return FakeAuthManager.auth_data.get(key, None)
-
class FakeRateLimiter(object):
def __init__(self, application):
diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py
index 60edce769..1bf49b33b 100644
--- a/nova/tests/api/openstack/test_accounts.py
+++ b/nova/tests/api/openstack/test_accounts.py
@@ -59,8 +59,8 @@ class AccountsTest(test.TestCase):
fakemgr = fakes.FakeAuthManager()
joeuser = User('guy1', 'guy1', 'acc1', 'fortytwo!', False)
superuser = User('guy2', 'guy2', 'acc2', 'swordfish', True)
- fakemgr.add_user(joeuser.access, joeuser)
- fakemgr.add_user(superuser.access, superuser)
+ fakemgr.add_user(joeuser)
+ fakemgr.add_user(superuser)
fakemgr.create_project('test1', joeuser)
fakemgr.create_project('test2', superuser)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index aaaa4e415..84222f0f1 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -39,7 +39,7 @@ class Test(test.TestCase):
self.stubs.Set(nova.api.openstack.auth.AuthMiddleware,
'__init__', fakes.fake_auth_init)
self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext)
- fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.clear_fakes()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_networking(self.stubs)
@@ -51,7 +51,7 @@ class Test(test.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
- f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
+ f.add_user(nova.auth.manager.User(1, 'herp', 'derp', None, None))
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'herp'
@@ -65,8 +65,8 @@ class Test(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'herp', None, None, None)
- f.add_user('derp', u)
+ u = nova.auth.manager.User(1, 'herp', 'derp', None, None)
+ f.add_user(u)
f.create_project('test', u)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
@@ -167,7 +167,7 @@ class TestLimiter(test.TestCase):
self.stubs.Set(nova.api.openstack.auth.AuthMiddleware,
'__init__', fakes.fake_auth_init)
self.stubs.Set(context, 'RequestContext', fakes.FakeRequestContext)
- fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.clear_fakes()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
@@ -178,8 +178,8 @@ class TestLimiter(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'herp', None, None, None)
- f.add_user('derp', u)
+ u = nova.auth.manager.User(1, 'herp', 'derp', None, None)
+ f.add_user(u)
f.create_project('test', u)
req = webob.Request.blank('/v1.0/')
diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py
index 2dda4319b..a62db7efc 100644
--- a/nova/tests/api/openstack/test_users.py
+++ b/nova/tests/api/openstack/test_users.py
@@ -47,7 +47,7 @@ class UsersTest(test.TestCase):
fake_init)
self.stubs.Set(nova.api.openstack.users.Controller, '_check_admin',
fake_admin_check)
- fakes.FakeAuthManager.auth_data = {}
+ fakes.FakeAuthManager.clear_fakes()
fakes.FakeAuthManager.projects = dict(testacct=Project('testacct',
'testacct',
'guy1',
@@ -61,10 +61,8 @@ class UsersTest(test.TestCase):
self.allow_admin = FLAGS.allow_admin_api
FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
- fakemgr.add_user('acc1', User('guy1', 'guy1', 'acc1',
- 'fortytwo!', False))
- fakemgr.add_user('acc2', User('guy2', 'guy2', 'acc2',
- 'swordfish', True))
+ fakemgr.add_user(User('guy1', 'guy1', 'acc1', 'fortytwo!', False))
+ fakemgr.add_user(User('guy2', 'guy2', 'acc2', 'swordfish', True))
def tearDown(self):
self.stubs.UnsetAll()
@@ -95,7 +93,7 @@ class UsersTest(test.TestCase):
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertTrue('guy1' not in [u.id for u in
- fakes.FakeAuthManager.auth_data.values()])
+ fakes.FakeAuthManager.auth_data])
self.assertEqual(res.status_int, 200)
def test_user_create(self):
@@ -118,8 +116,8 @@ class UsersTest(test.TestCase):
self.assertEqual(res_dict['user']['secret'], 'invasionIsInNormandy')
self.assertEqual(res_dict['user']['admin'], True)
self.assertTrue('test_guy' in [u.id for u in
- fakes.FakeAuthManager.auth_data.values()])
- self.assertEqual(len(fakes.FakeAuthManager.auth_data.values()), 3)
+ fakes.FakeAuthManager.auth_data])
+ self.assertEqual(len(fakes.FakeAuthManager.auth_data), 3)
def test_user_update(self):
body = dict(user=dict(name='guy2',
--
cgit
From 909b72faa77ba0a2bc787309b95fdfae9bb9ca01 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Fri, 11 Mar 2011 17:41:10 -0500
Subject: Removed EOL whitespace in accordance with PEP-8.
---
nova/tests/api/openstack/test_auth.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index d1b3c0e10..0448ed701 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -51,7 +51,7 @@ class Test(test.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
- f.add_user('user1_key',
+ f.add_user('user1_key',
nova.auth.manager.User(1, 'user1', None, None, None))
req = webob.Request.blank('/v1.0/')
--
cgit
From b3f5a4d5a8e513fe65a3b1dde9b36fd1388afb67 Mon Sep 17 00:00:00 2001
From: "Kevin L. Mitchell"
Date: Fri, 11 Mar 2011 22:55:56 +0000
Subject: Remove vish comment
---
nova/db/sqlalchemy/api.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 08bc8fe2f..71b85d659 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -578,8 +578,6 @@ def fixed_ip_disassociate(context, address):
@require_admin_context
def fixed_ip_disassociate_all_by_timeout(_context, host, time):
session = get_session()
- # NOTE(vish): The nested select is because sqlite doesn't support
- # JOINs in UPDATEs.
inner_q = session.query(models.Network.id).\
filter_by(host=host).\
subquery()
--
cgit
From da76b3d67b2c2e864025c4ba201b63e1dee2ff1f Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 11 Mar 2011 16:58:18 -0600
Subject: Review feedback
---
nova/virt/xenapi/vm_utils.py | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index a42cabfe4..866eb5d62 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -101,7 +101,7 @@ class VMHelper(HelperBase):
get_instance_type(instance.instance_type)
mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
vcpus = str(instance_type['vcpus'])
- vm_rec = {
+ rec = {
'actions_after_crash': 'destroy',
'actions_after_reboot': 'restart',
'actions_after_shutdown': 'destroy',
@@ -145,21 +145,21 @@ class VMHelper(HelperBase):
vm_rec['platform']['nx'] = 'false'
if instance.kernel_id:
# 1. Kernel explicitly passed in, use that
- vm_rec['PV_args'] = 'root=/dev/xvda1'
- vm_rec['PV_kernel'] = kernel
- vm_rec['PV_ramdisk'] = ramdisk
+ rec['PV_args'] = 'root=/dev/xvda1'
+ rec['PV_kernel'] = kernel
+ rec['PV_ramdisk'] = ramdisk
else:
# 2. Use kernel within the image
- vm_rec['PV_args'] = 'clocksource=jiffies'
- vm_rec['PV_bootloader'] = 'pygrub'
+ rec['PV_args'] = 'clocksource=jiffies'
+ rec['PV_bootloader'] = 'pygrub'
else:
# 3. Using hardware virtualization
- vm_rec['platform']['nx'] = 'true'
- vm_rec['HVM_boot_params'] = {'order': 'dc'}
- vm_rec['HVM_boot_policy'] = 'BIOS order'
+ rec['platform']['nx'] = 'true'
+ rec['HVM_boot_params'] = {'order': 'dc'}
+ rec['HVM_boot_policy'] = 'BIOS order'
LOG.debug(_('Created VM %s...'), instance.name)
- vm_ref = session.call_xenapi('VM.create', vm_rec)
+ vm_ref = session.call_xenapi('VM.create', rec)
instance_name = instance.name
LOG.debug(_('Created VM %(instance_name)s as %(vm_ref)s.') % locals())
return vm_ref
--
cgit
From 80bc32659e41f496bb1bfefbdd6ca63de7ff9f98 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 11 Mar 2011 17:11:25 -0600
Subject: Review feedback
---
nova/virt/xenapi/vmops.py | 61 +++++++++++++++++++++++++----------------------
1 file changed, 33 insertions(+), 28 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 190c2022d..3ccdf9d80 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -434,7 +434,7 @@ class VMOps(object):
raise RuntimeError(resp_dict['message'])
return resp_dict['message']
- def _shutdown(self, instance, vm, hard=True):
+ def _shutdown(self, instance, vm_ref, hard=True):
"""Shutdown an instance"""
state = self.get_info(instance['name'])['state']
if state == power_state.SHUTDOWN:
@@ -448,31 +448,33 @@ class VMOps(object):
try:
task = None
if hard:
- task = self._session.call_xenapi("Async.VM.hard_shutdown", vm)
+ task = self._session.call_xenapi("Async.VM.hard_shutdown",
+ vm_ref)
else:
- task = self._session.call_xenapi('Async.VM.clean_shutdown', vm)
+ task = self._session.call_xenapi("Async.VM.clean_shutdown",
+ vm_ref)
self._session.wait_for_task(task, instance.id)
except self.XenAPI.Failure, exc:
LOG.exception(exc)
- def _destroy_vdis(self, instance, vm):
- """Destroys all VDIs associated with a VM """
+ def _destroy_vdis(self, instance, vm_ref):
+ """Destroys all VDIs associated with a VM"""
instance_id = instance.id
LOG.debug(_("Destroying VDIs for Instance %(instance_id)s")
% locals())
- vdis = VMHelper.lookup_vm_vdis(self._session, vm)
+ vdi_refs = VMHelper.lookup_vm_vdis(self._session, vm_ref)
- if not vdis:
+ if not vdi_refs:
return
- for vdi in vdis:
+ for vdi_ref in vdi_refs:
try:
- task = self._session.call_xenapi('Async.VDI.destroy', vdi)
+ task = self._session.call_xenapi('Async.VDI.destroy', vdi_ref)
self._session.wait_for_task(task, instance.id)
except self.XenAPI.Failure, exc:
LOG.exception(exc)
- def _destroy_kernel_ramdisk(self, instance, vm):
+ def _destroy_kernel_ramdisk(self, instance, vm_ref):
"""
Three situations can occur:
@@ -499,8 +501,8 @@ class VMOps(object):
"both" % locals()))
# 3. We have both kernel and ramdisk
- (kernel, ramdisk) = VMHelper.lookup_kernel_ramdisk(
- self._session, vm)
+ (kernel, ramdisk) = VMHelper.lookup_kernel_ramdisk(self._session,
+ vm_ref)
LOG.debug(_("Removing kernel/ramdisk files"))
@@ -511,11 +513,11 @@ class VMOps(object):
LOG.debug(_("kernel/ramdisk files removed"))
- def _destroy_vm(self, instance, vm):
- """Destroys a VM record """
+ def _destroy_vm(self, instance, vm_ref):
+ """Destroys a VM record"""
instance_id = instance.id
try:
- task = self._session.call_xenapi('Async.VM.destroy', vm)
+ task = self._session.call_xenapi('Async.VM.destroy', vm_ref)
self._session.wait_for_task(task, instance_id)
except self.XenAPI.Failure, exc:
LOG.exception(exc)
@@ -596,8 +598,9 @@ class VMOps(object):
- spawn a rescue VM (the vm name-label will be instance-N-rescue)
"""
- rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue")
- if rescue_vm:
+ rescue_vm_ref = VMHelper.lookup(self._session,
+ instance.name + "-rescue")
+ if rescue_vm_ref:
raise RuntimeError(_(
"Instance is already in Rescue Mode: %s" % instance.name))
@@ -609,8 +612,8 @@ class VMOps(object):
self.spawn(instance)
rescue_vm_ref = self._get_vm_opaque_ref(instance)
- vbd = self._session.get_xenapi().VM.get_VBDs(vm_ref)[0]
- vdi_ref = self._session.get_xenapi().VBD.get_record(vbd)["VDI"]
+ vbd_ref = self._session.get_xenapi().VM.get_VBDs(vm_ref)[0]
+ vdi_ref = self._session.get_xenapi().VBD.get_record(vbd_ref)["VDI"]
vbd_ref = VMHelper.create_vbd(self._session, rescue_vm_ref, vdi_ref,
1, False)
@@ -623,35 +626,37 @@ class VMOps(object):
- release the bootlock to allow the instance VM to start
"""
- rescue_vm = VMHelper.lookup(self._session, instance.name + "-rescue")
+ rescue_vm_ref = VMHelper.lookup(self._session,
+ instance.name + "-rescue")
- if not rescue_vm:
+ if not rescue_vm_ref:
raise exception.NotFound(_(
"Instance is not in Rescue Mode: %s" % instance.name))
original_vm_ref = self._get_vm_opaque_ref(instance)
- vbds = self._session.get_xenapi().VM.get_VBDs(rescue_vm)
+ vbd_refs = self._session.get_xenapi().VM.get_VBDs(rescue_vm_ref)
instance._rescue = False
- for vbd_ref in vbds:
+ for vbd_ref in vbd_refs:
vbd = self._session.get_xenapi().VBD.get_record(vbd_ref)
if vbd["userdevice"] == "1":
VMHelper.unplug_vbd(self._session, vbd_ref)
VMHelper.destroy_vbd(self._session, vbd_ref)
- task1 = self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm)
+ task1 = self._session.call_xenapi("Async.VM.hard_shutdown",
+ rescue_vm_ref)
self._session.wait_for_task(task1, instance.id)
- vdis = VMHelper.lookup_vm_vdis(self._session, rescue_vm)
- for vdi in vdis:
+ vdi_refs = VMHelper.lookup_vm_vdis(self._session, rescue_vm_ref)
+ for vdi_ref in vdi_refs:
try:
- task = self._session.call_xenapi('Async.VDI.destroy', vdi)
+ task = self._session.call_xenapi('Async.VDI.destroy', vdi_ref)
self._session.wait_for_task(task, instance.id)
except self.XenAPI.Failure:
continue
- task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm)
+ task2 = self._session.call_xenapi('Async.VM.destroy', rescue_vm_ref)
self._session.wait_for_task(task2, instance.id)
self._release_bootlock(original_vm_ref)
--
cgit
From ab37248cc2e40b06e1d349833da01494a9ca3641 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 11 Mar 2011 17:13:10 -0600
Subject: oops
---
nova/virt/xenapi/vm_utils.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 866eb5d62..8dd246178 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -142,7 +142,7 @@ class VMHelper(HelperBase):
# Complete VM configuration record according to the image type
# non-raw/raw with PV kernel/raw in HVM mode
if use_pv_kernel:
- vm_rec['platform']['nx'] = 'false'
+ rec['platform']['nx'] = 'false'
if instance.kernel_id:
# 1. Kernel explicitly passed in, use that
rec['PV_args'] = 'root=/dev/xvda1'
--
cgit
From bacce305a99dff77aa0e24c64cb937514e368ec1 Mon Sep 17 00:00:00 2001
From: Anne Gentle
Date: Fri, 11 Mar 2011 17:13:56 -0600
Subject: Adding a sidebar element to the nova.openstack.org site to point
people to additional versions of the site.
---
doc/source/_static/tweaks.css | 147 ++++++++++++++++++++++++++++++++++++++++++
doc/source/_theme/layout.html | 11 +++-
2 files changed, 157 insertions(+), 1 deletion(-)
diff --git a/doc/source/_static/tweaks.css b/doc/source/_static/tweaks.css
index 1a18dbac6..7c57c8f35 100644
--- a/doc/source/_static/tweaks.css
+++ b/doc/source/_static/tweaks.css
@@ -69,3 +69,150 @@ table.docutils {
.tweet_list li .tweet_avatar {
float: left;
}
+
+/* ------------------------------------------
+PURE CSS SPEECH BUBBLES
+by Nicolas Gallagher
+- http://nicolasgallagher.com/pure-css-speech-bubbles/
+
+http://nicolasgallagher.com
+http://twitter.com/necolas
+
+Created: 02 March 2010
+Version: 1.1 (21 October 2010)
+
+Dual licensed under MIT and GNU GPLv2 © Nicolas Gallagher
+------------------------------------------ */
+/* THE SPEECH BUBBLE
+------------------------------------------------------------------------------------------------------------------------------- */
+
+/* THE SPEECH BUBBLE
+------------------------------------------------------------------------------------------------------------------------------- */
+
+.triangle-border {
+ position:relative;
+ padding:15px;
+ margin:1em 0 3em;
+ border:5px solid #BC1518;
+ color:#333;
+ background:#fff;
+
+ /* css3 */
+ -moz-border-radius:10px;
+ -webkit-border-radius:10px;
+ border-radius:10px;
+}
+
+/* Variant : for left positioned triangle
+------------------------------------------ */
+
+.triangle-border.left {
+ margin-left:30px;
+}
+
+/* Variant : for right positioned triangle
+------------------------------------------ */
+
+.triangle-border.right {
+ margin-right:30px;
+}
+
+/* THE TRIANGLE
+------------------------------------------------------------------------------------------------------------------------------- */
+
+.triangle-border:before {
+ content:"";
+ display:block; /* reduce the damage in FF3.0 */
+ position:absolute;
+ bottom:-40px; /* value = - border-top-width - border-bottom-width */
+ left:40px; /* controls horizontal position */
+ width:0;
+ height:0;
+ border:20px solid transparent;
+ border-top-color:#BC1518;
+}
+
+/* creates the smaller triangle */
+.triangle-border:after {
+ content:"";
+ display:block; /* reduce the damage in FF3.0 */
+ position:absolute;
+ bottom:-26px; /* value = - border-top-width - border-bottom-width */
+ left:47px; /* value = (:before left) + (:before border-left) - (:after border-left) */
+ width:0;
+ height:0;
+ border:13px solid transparent;
+ border-top-color:#fff;
+}
+
+/* Variant : top
+------------------------------------------ */
+
+/* creates the larger triangle */
+.triangle-border.top:before {
+ top:-40px; /* value = - border-top-width - border-bottom-width */
+ right:40px; /* controls horizontal position */
+ bottom:auto;
+ left:auto;
+ border:20px solid transparent;
+ border-bottom-color:#BC1518;
+}
+
+/* creates the smaller triangle */
+.triangle-border.top:after {
+ top:-26px; /* value = - border-top-width - border-bottom-width */
+ right:47px; /* value = (:before right) + (:before border-right) - (:after border-right) */
+ bottom:auto;
+ left:auto;
+ border:13px solid transparent;
+ border-bottom-color:#fff;
+}
+
+/* Variant : left
+------------------------------------------ */
+
+/* creates the larger triangle */
+.triangle-border.left:before {
+ top:10px; /* controls vertical position */
+ left:-30px; /* value = - border-left-width - border-right-width */
+ bottom:auto;
+ border-width:15px 30px 15px 0;
+ border-style:solid;
+ border-color:transparent #BC1518;
+}
+
+/* creates the smaller triangle */
+.triangle-border.left:after {
+ top:16px; /* value = (:before top) + (:before border-top) - (:after border-top) */
+ left:-21px; /* value = - border-left-width - border-right-width */
+ bottom:auto;
+ border-width:9px 21px 9px 0;
+ border-style:solid;
+ border-color:transparent #fff;
+}
+
+/* Variant : right
+------------------------------------------ */
+
+/* creates the larger triangle */
+.triangle-border.right:before {
+ top:10px; /* controls vertical position */
+ right:-30px; /* value = - border-left-width - border-right-width */
+ bottom:auto;
+ left:auto;
+ border-width:15px 0 15px 30px;
+ border-style:solid;
+ border-color:transparent #BC1518;
+}
+
+/* creates the smaller triangle */
+.triangle-border.right:after {
+ top:16px; /* value = (:before top) + (:before border-top) - (:after border-top) */
+ right:-21px; /* value = - border-left-width - border-right-width */
+ bottom:auto;
+ left:auto;
+ border-width:9px 0 9px 21px;
+ border-style:solid;
+ border-color:transparent #fff;
+}
+
diff --git a/doc/source/_theme/layout.html b/doc/source/_theme/layout.html
index e3eb54b71..958c512e4 100644
--- a/doc/source/_theme/layout.html
+++ b/doc/source/_theme/layout.html
@@ -71,12 +71,21 @@
+
+
+ Psst... hey. You're reading the latest content, but it might be out of sync with code. You can read Nova 2011.1 docs or all OpenStack docs too.
+
+
{%- endif %}
{%- if pagename == "index" %}
- {{ _('Twitter Feed') }}
+
+
+ {{ _('Twitter Feed') }}
{%- endif %}
+
+
{%- endblock %}
--
cgit
From 29e59a96602265c5b5746865db94a3f00b8b5cf5 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Fri, 11 Mar 2011 15:21:31 -0800
Subject: Change capitalization of Openstack to OpenStack
---
nova/tests/integrated/api/client.py | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index da8d87e07..245eb8c69 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -24,7 +24,7 @@ from nova import log as logging
LOG = logging.getLogger('nova.tests.api')
-class OpenstackApiException(Exception):
+class OpenStackApiException(Exception):
def __init__(self, message=None, response=None):
self.response = response
if not message:
@@ -37,22 +37,22 @@ class OpenstackApiException(Exception):
message = _('%(message)s\nStatus Code: %(_status)s\n'
'Body: %(_body)s') % locals()
- super(OpenstackApiException, self).__init__(message)
+ super(OpenStackApiException, self).__init__(message)
-class OpenstackApiAuthenticationException(OpenstackApiException):
+class OpenStackApiAuthenticationException(OpenStackApiException):
def __init__(self, response=None, message=None):
if not message:
message = _("Authentication error")
- super(OpenstackApiAuthenticationException, self).__init__(message,
+ super(OpenStackApiAuthenticationException, self).__init__(message,
response)
-class OpenstackApiNotFoundException(OpenstackApiException):
+class OpenStackApiNotFoundException(OpenStackApiException):
def __init__(self, response=None, message=None):
if not message:
message = _("Item not found")
- super(OpenstackApiNotFoundException, self).__init__(message, response)
+ super(OpenStackApiNotFoundException, self).__init__(message, response)
class TestOpenStackClient(object):
@@ -82,7 +82,7 @@ class TestOpenStackClient(object):
conn = httplib.HTTPSConnection(hostname,
port=port)
else:
- raise OpenstackApiException("Unknown scheme: %s" % url)
+ raise OpenStackApiException("Unknown scheme: %s" % url)
relative_url = parsed_url.path
if parsed_url.query:
@@ -111,7 +111,7 @@ class TestOpenStackClient(object):
# Until bug732866 is fixed, we can't check this properly...
#if http_status == 401:
if http_status != 204:
- raise OpenstackApiAuthenticationException(response=response)
+ raise OpenStackApiAuthenticationException(response=response)
auth_headers = {}
for k, v in response.getheaders():
@@ -138,9 +138,9 @@ class TestOpenStackClient(object):
if check_response_status:
if not http_status in check_response_status:
if http_status == 404:
- raise OpenstackApiNotFoundException(response=response)
+ raise OpenStackApiNotFoundException(response=response)
else:
- raise OpenstackApiException(
+ raise OpenStackApiException(
message=_("Unexpected status code"),
response=response)
--
cgit
From f9706b5080786a4d3e530f3e8bdb69147e9f5086 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 11 Mar 2011 17:35:37 -0600
Subject: Review feedback
---
nova/virt/xenapi/vm_utils.py | 31 ++++++++++++++++---------------
nova/virt/xenapi/vmops.py | 6 +++---
2 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 8dd246178..7aa3f3c3b 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -633,37 +633,38 @@ class VMHelper(HelperBase):
return is_pv
@classmethod
- def lookup(cls, session, i):
+ def lookup(cls, session, name_label):
"""Look the instance i up, and returns it if available"""
- vms = session.get_xenapi().VM.get_by_name_label(i)
- n = len(vms)
+ vm_refs = session.get_xenapi().VM.get_by_name_label(name_label)
+ n = len(vm_refs)
if n == 0:
return None
elif n > 1:
- raise exception.Duplicate(_('duplicate name found: %s') % i)
+ raise exception.Duplicate(_('duplicate name found: %s') %
+ name_label)
else:
- return vms[0]
+ return vm_refs[0]
@classmethod
- def lookup_vm_vdis(cls, session, vm):
+ def lookup_vm_vdis(cls, session, vm_ref):
"""Look for the VDIs that are attached to the VM"""
# Firstly we get the VBDs, then the VDIs.
# TODO(Armando): do we leave the read-only devices?
- vbds = session.get_xenapi().VM.get_VBDs(vm)
- vdis = []
- if vbds:
- for vbd in vbds:
+ vbd_refs = session.get_xenapi().VM.get_VBDs(vm_ref)
+ vdi_refs = []
+ if vbd_refs:
+ for vbd_ref in vbd_refs:
try:
- vdi = session.get_xenapi().VBD.get_VDI(vbd)
+ vdi_ref = session.get_xenapi().VBD.get_VDI(vbd_ref)
# Test valid VDI
- record = session.get_xenapi().VDI.get_record(vdi)
+ record = session.get_xenapi().VDI.get_record(vdi_ref)
LOG.debug(_('VDI %s is still available'), record['uuid'])
except cls.XenAPI.Failure, exc:
LOG.exception(exc)
else:
- vdis.append(vdi)
- if len(vdis) > 0:
- return vdis
+ vdi_refs.append(vdi_ref)
+ if len(vdi_refs) > 0:
+ return vdi_refs
else:
return None
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 3ccdf9d80..0faec1169 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -614,10 +614,10 @@ class VMOps(object):
vbd_ref = self._session.get_xenapi().VM.get_VBDs(vm_ref)[0]
vdi_ref = self._session.get_xenapi().VBD.get_record(vbd_ref)["VDI"]
- vbd_ref = VMHelper.create_vbd(self._session, rescue_vm_ref, vdi_ref,
- 1, False)
+ rescue_vbd_ref = VMHelper.create_vbd(self._session, rescue_vm_ref,
+ vdi_ref, 1, False)
- self._session.call_xenapi("Async.VBD.plug", vbd_ref)
+ self._session.call_xenapi("Async.VBD.plug", rescue_vbd_ref)
def unrescue(self, instance, callback):
"""Unrescue the specified instance
--
cgit
From cdd8790426d3eb77712f5a19f99211b12a9ad9c5 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 11 Mar 2011 17:48:44 -0600
Subject: Review feedback
---
nova/virt/xenapi/vm_utils.py | 10 +++++-----
nova/virt/xenapi/vmops.py | 6 +++---
nova/virt/xenapi/volume_utils.py | 6 +++---
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 7aa3f3c3b..1a872345d 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -202,13 +202,13 @@ class VMHelper(HelperBase):
@classmethod
def find_vbd_by_number(cls, session, vm_ref, number):
"""Get the VBD reference from the device number"""
- vbds = session.get_xenapi().VM.get_VBDs(vm_ref)
- if vbds:
- for vbd in vbds:
+ vbd_refs = session.get_xenapi().VM.get_VBDs(vm_ref)
+ if vbd_refs:
+ for vbd_ref in vbd_refs:
try:
- vbd_rec = session.get_xenapi().VBD.get_record(vbd)
+ vbd_rec = session.get_xenapi().VBD.get_record(vbd_ref)
if vbd_rec['userdevice'] == str(number):
- return vbd
+ return vbd_ref
except cls.XenAPI.Failure, exc:
LOG.exception(exc)
raise StorageError(_('VBD not found in instance %s') % vm_ref)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 0faec1169..382915b0c 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -55,12 +55,12 @@ class VMOps(object):
def list_instances(self):
"""List VM instances"""
- vms = []
+ vm_refs = []
for vm_ref in self._session.get_xenapi().VM.get_all():
vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
if not vm_rec["is_a_template"] and not vm_rec["is_control_domain"]:
- vms.append(vm_rec["name_label"])
- return vms
+ vm_refs.append(vm_rec["name_label"])
+ return vm_refs
def _start(self, instance, vm_ref=None):
"""Power on a VM instance"""
diff --git a/nova/virt/xenapi/volume_utils.py b/nova/virt/xenapi/volume_utils.py
index d5ebd29d5..72284ac02 100644
--- a/nova/virt/xenapi/volume_utils.py
+++ b/nova/virt/xenapi/volume_utils.py
@@ -117,16 +117,16 @@ class VolumeHelper(HelperBase):
def introduce_vdi(cls, session, sr_ref):
"""Introduce VDI in the host"""
try:
- vdis = session.get_xenapi().SR.get_VDIs(sr_ref)
+ vdi_refs = session.get_xenapi().SR.get_VDIs(sr_ref)
except cls.XenAPI.Failure, exc:
LOG.exception(exc)
raise StorageError(_('Unable to introduce VDI on SR %s') % sr_ref)
try:
- vdi_rec = session.get_xenapi().VDI.get_record(vdis[0])
+ vdi_rec = session.get_xenapi().VDI.get_record(vdi_refs[0])
except cls.XenAPI.Failure, exc:
LOG.exception(exc)
raise StorageError(_('Unable to get record'
- ' of VDI %s on') % vdis[0])
+ ' of VDI %s on') % vdi_refs[0])
else:
try:
return session.get_xenapi().VDI.introduce(
--
cgit
From 48196abaf7e9c47bfda3f744e0be9bc242004b72 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 11 Mar 2011 18:00:34 -0600
Subject: Review feedback
---
nova/virt/xenapi/vm_utils.py | 56 ++++++++++++++++++++++----------------------
nova/virt/xenapi/vmops.py | 8 +++----
2 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 1a872345d..4d55937e3 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -443,29 +443,29 @@ class VMHelper(HelperBase):
vdi_size += MBR_SIZE_BYTES
name_label = get_name_label_for_image(image)
- vdi = cls.create_vdi(session, sr_ref, name_label, vdi_size, False)
+ vdi_ref = cls.create_vdi(session, sr_ref, name_label, vdi_size, False)
- with_vdi_attached_here(session, vdi, False,
+ with_vdi_attached_here(session, vdi_ref, False,
lambda dev:
_stream_disk(dev, image_type,
virtual_size, image_file))
if image_type == ImageType.KERNEL_RAMDISK:
#we need to invoke a plugin for copying VDI's
#content into proper path
- LOG.debug(_("Copying VDI %s to /boot/guest on dom0"), vdi)
+ LOG.debug(_("Copying VDI %s to /boot/guest on dom0"), vdi_ref)
fn = "copy_kernel_vdi"
args = {}
- args['vdi-ref'] = vdi
+ args['vdi-ref'] = vdi_ref
#let the plugin copy the correct number of bytes
args['image-size'] = str(vdi_size)
task = session.async_call_plugin('glance', fn, args)
filename = session.wait_for_task(task, instance_id)
#remove the VDI as it is not needed anymore
- session.get_xenapi().VDI.destroy(vdi)
- LOG.debug(_("Kernel/Ramdisk VDI %s destroyed"), vdi)
+ session.get_xenapi().VDI.destroy(vdi_ref)
+ LOG.debug(_("Kernel/Ramdisk VDI %s destroyed"), vdi_ref)
return filename
else:
- return session.get_xenapi().VDI.get_uuid(vdi)
+ return session.get_xenapi().VDI.get_uuid(vdi_ref)
@classmethod
def determine_disk_image_type(cls, instance):
@@ -840,16 +840,16 @@ def safe_find_sr(session):
def find_sr(session):
"""Return the storage repository to hold VM images"""
host = session.get_xenapi_host()
- srs = session.get_xenapi().SR.get_all()
- for sr in srs:
- sr_rec = session.get_xenapi().SR.get_record(sr)
+ sr_refs = session.get_xenapi().SR.get_all()
+ for sr_ref in sr_refs:
+ sr_rec = session.get_xenapi().SR.get_record(sr_ref)
if not ('i18n-key' in sr_rec['other_config'] and
sr_rec['other_config']['i18n-key'] == 'local-storage'):
continue
- for pbd in sr_rec['PBDs']:
- pbd_rec = session.get_xenapi().PBD.get_record(pbd)
+ for pbd_ref in sr_rec['PBDs']:
+ pbd_rec = session.get_xenapi().PBD.get_record(pbd_ref)
if pbd_rec['host'] == host:
- return sr
+ return sr_ref
return None
@@ -874,11 +874,11 @@ def remap_vbd_dev(dev):
return remapped_dev
-def with_vdi_attached_here(session, vdi, read_only, f):
+def with_vdi_attached_here(session, vdi_ref, read_only, f):
this_vm_ref = get_this_vm_ref(session)
vbd_rec = {}
vbd_rec['VM'] = this_vm_ref
- vbd_rec['VDI'] = vdi
+ vbd_rec['VDI'] = vdi_ref
vbd_rec['userdevice'] = 'autodetect'
vbd_rec['bootable'] = False
vbd_rec['mode'] = read_only and 'RO' or 'RW'
@@ -889,14 +889,14 @@ def with_vdi_attached_here(session, vdi, read_only, f):
vbd_rec['qos_algorithm_type'] = ''
vbd_rec['qos_algorithm_params'] = {}
vbd_rec['qos_supported_algorithms'] = []
- LOG.debug(_('Creating VBD for VDI %s ... '), vdi)
- vbd = session.get_xenapi().VBD.create(vbd_rec)
- LOG.debug(_('Creating VBD for VDI %s done.'), vdi)
+ LOG.debug(_('Creating VBD for VDI %s ... '), vdi_ref)
+ vbd_ref = session.get_xenapi().VBD.create(vbd_rec)
+ LOG.debug(_('Creating VBD for VDI %s done.'), vdi_ref)
try:
- LOG.debug(_('Plugging VBD %s ... '), vbd)
- session.get_xenapi().VBD.plug(vbd)
- LOG.debug(_('Plugging VBD %s done.'), vbd)
- orig_dev = session.get_xenapi().VBD.get_device(vbd)
+ LOG.debug(_('Plugging VBD %s ... '), vbd_ref)
+ session.get_xenapi().VBD.plug(vbd_ref)
+ LOG.debug(_('Plugging VBD %s done.'), vbd_ref)
+ orig_dev = session.get_xenapi().VBD.get_device(vbd_ref)
LOG.debug(_('VBD %(vbd)s plugged as %(orig_dev)s') % locals())
dev = remap_vbd_dev(orig_dev)
if dev != orig_dev:
@@ -904,13 +904,13 @@ def with_vdi_attached_here(session, vdi, read_only, f):
'remapping to %(dev)s') % locals())
return f(dev)
finally:
- LOG.debug(_('Destroying VBD for VDI %s ... '), vdi)
- vbd_unplug_with_retry(session, vbd)
- ignore_failure(session.get_xenapi().VBD.destroy, vbd)
- LOG.debug(_('Destroying VBD for VDI %s done.'), vdi)
+ LOG.debug(_('Destroying VBD for VDI %s ... '), vdi_ref)
+ vbd_unplug_with_retry(session, vbd_ref)
+ ignore_failure(session.get_xenapi().VBD.destroy, vbd_ref)
+ LOG.debug(_('Destroying VBD for VDI %s done.'), vdi_ref)
-def vbd_unplug_with_retry(session, vbd):
+def vbd_unplug_with_retry(session, vbd_ref):
"""Call VBD.unplug on the given VBD, with a retry if we get
DEVICE_DETACH_REJECTED. For reasons which I don't understand, we're
seeing the device still in use, even when all processes using the device
@@ -918,7 +918,7 @@ def vbd_unplug_with_retry(session, vbd):
# FIXME(sirp): We can use LoopingCall here w/o blocking sleep()
while True:
try:
- session.get_xenapi().VBD.unplug(vbd)
+ session.get_xenapi().VBD.unplug(vbd_ref)
LOG.debug(_('VBD.unplug successful first time.'))
return
except VMHelper.XenAPI.Failure, e:
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 382915b0c..fcb290d03 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -87,8 +87,8 @@ class VMOps(object):
def _spawn_with_disk(self, instance, vdi_uuid):
"""Create VM instance"""
instance_name = instance.name
- vm = VMHelper.lookup(self._session, instance_name)
- if vm is not None:
+ vm_ref = VMHelper.lookup(self._session, instance_name)
+ if vm_ref is not None:
raise exception.Duplicate(_('Attempted to create'
' non-unique name %s') % instance_name)
@@ -639,8 +639,8 @@ class VMOps(object):
instance._rescue = False
for vbd_ref in vbd_refs:
- vbd = self._session.get_xenapi().VBD.get_record(vbd_ref)
- if vbd["userdevice"] == "1":
+ _vbd_ref = self._session.get_xenapi().VBD.get_record(vbd_ref)
+ if _vbd_ref["userdevice"] == "1":
VMHelper.unplug_vbd(self._session, vbd_ref)
VMHelper.destroy_vbd(self._session, vbd_ref)
--
cgit
From b944cbcbf023ca321edcc511354b56aa5b07d438 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 11 Mar 2011 18:03:19 -0600
Subject: oops
---
nova/virt/xenapi/vm_utils.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 4d55937e3..f07b57796 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -897,10 +897,10 @@ def with_vdi_attached_here(session, vdi_ref, read_only, f):
session.get_xenapi().VBD.plug(vbd_ref)
LOG.debug(_('Plugging VBD %s done.'), vbd_ref)
orig_dev = session.get_xenapi().VBD.get_device(vbd_ref)
- LOG.debug(_('VBD %(vbd)s plugged as %(orig_dev)s') % locals())
+ LOG.debug(_('VBD %(vbd_ref)s plugged as %(orig_dev)s') % locals())
dev = remap_vbd_dev(orig_dev)
if dev != orig_dev:
- LOG.debug(_('VBD %(vbd)s plugged into wrong dev, '
+ LOG.debug(_('VBD %(vbd_ref)s plugged into wrong dev, '
'remapping to %(dev)s') % locals())
return f(dev)
finally:
--
cgit
From 87f7356e98dbb4d01305785ed8209f44b525ff2c Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Fri, 11 Mar 2011 19:21:34 -0500
Subject: Removed _translate_keys() functions since it is no longer used. Moved
private top level functions to bottom of module.
---
nova/api/openstack/servers.py | 148 ++++++++++++++++++++----------------------
1 file changed, 71 insertions(+), 77 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index b486dfebb..940c2c47e 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -34,82 +34,9 @@ import nova.api.openstack
LOG = logging.getLogger('server')
-
-
FLAGS = flags.FLAGS
-def _translate_keys(req, inst):
- """ Coerces into dictionary format, excluding all model attributes
- save for id and name """
- return dict(server=dict(id=inst['id'], name=inst['display_name']))
-
-
-def _build_addresses_10(inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- return dict(public=public_ips, private=private_ips)
-
-
-def _build_addresses_11(inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- private_ips = [dict(version=4, addr=a) for a in private_ips]
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- public_ips = [dict(version=4, addr=a) for a in public_ips]
- return dict(public=public_ips, private=private_ips)
-
-
-def addresses_builder(req):
- version = req.environ['nova.context'].version
- if version == '1.1':
- return _build_addresses_11
- else:
- return _build_addresses_10
-
-
-def build_server(req, inst, is_detail):
- """ Coerces into dictionary format, mapping everything to Rackspace-like
- attributes for return"""
-
- if not is_detail:
- return dict(server=dict(id=inst['id'], name=inst['display_name']))
-
- power_mapping = {
- None: 'build',
- power_state.NOSTATE: 'build',
- power_state.RUNNING: 'active',
- power_state.BLOCKED: 'active',
- power_state.SUSPENDED: 'suspended',
- power_state.PAUSED: 'paused',
- power_state.SHUTDOWN: 'active',
- power_state.SHUTOFF: 'active',
- power_state.CRASHED: 'error',
- power_state.FAILED: 'error'}
- inst_dict = {}
- version = req.environ['nova.context'].version
-
- mapped_keys = dict(status='state', imageId='image_id',
- flavorId='instance_type', name='display_name', id='id')
-
- for k, v in mapped_keys.iteritems():
- inst_dict[k] = inst[v]
-
- inst_dict['status'] = power_mapping[inst_dict['status']]
- inst_dict['addresses'] = addresses_builder(req)(inst)
-
- # Return the metadata as a dictionary
- metadata = {}
- for item in inst['metadata']:
- metadata[item['key']] = item['value']
- inst_dict['metadata'] = metadata
-
- inst_dict['hostId'] = ''
- if inst['host']:
- inst_dict['hostId'] = hashlib.sha224(inst['host']).hexdigest()
-
- return dict(server=inst_dict)
-
-
class Controller(wsgi.Controller):
""" The Server API controller for the OpenStack API """
@@ -127,7 +54,7 @@ class Controller(wsgi.Controller):
def ips(self, req, id):
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- return addresses_builder(req)(instance)
+ return _addresses_builder(req)(instance)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -146,7 +73,7 @@ class Controller(wsgi.Controller):
"""
instance_list = self.compute_api.get_all(req.environ['nova.context'])
limited_list = common.limited(instance_list, req)
- res = [build_server(req, inst, is_detail)['server']
+ res = [_build_server(req, inst, is_detail)['server']
for inst in limited_list]
return dict(servers=res)
@@ -154,7 +81,7 @@ class Controller(wsgi.Controller):
""" Returns server details by server id """
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- return build_server(req, instance, is_detail=True)
+ return _build_server(req, instance, is_detail=True)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -206,7 +133,7 @@ class Controller(wsgi.Controller):
metadata=metadata,
onset_files=env.get('onset_files', []))
- server = build_server(req, instances[0], is_detail=False)
+ server = _build_server(req, instances[0], is_detail=False)
password = "%s%s" % (server['server']['name'][:4],
utils.generate_password(12))
server['server']['adminPass'] = password
@@ -503,3 +430,70 @@ class Controller(wsgi.Controller):
_("Ramdisk not found for image %(image_id)s") % locals())
return kernel_id, ramdisk_id
+
+
+def _build_server(req, inst, is_detail):
+ """ Coerces into dictionary format, mapping everything to Rackspace-like
+ attributes for return"""
+
+ if not is_detail:
+ return dict(server=dict(id=inst['id'], name=inst['display_name']))
+
+ power_mapping = {
+ None: 'build',
+ power_state.NOSTATE: 'build',
+ power_state.RUNNING: 'active',
+ power_state.BLOCKED: 'active',
+ power_state.SUSPENDED: 'suspended',
+ power_state.PAUSED: 'paused',
+ power_state.SHUTDOWN: 'active',
+ power_state.SHUTOFF: 'active',
+ power_state.CRASHED: 'error',
+ power_state.FAILED: 'error'}
+ inst_dict = {}
+ version = req.environ['nova.context'].version
+
+ mapped_keys = dict(status='state', imageId='image_id',
+ flavorId='instance_type', name='display_name', id='id')
+
+ for k, v in mapped_keys.iteritems():
+ inst_dict[k] = inst[v]
+
+ inst_dict['status'] = power_mapping[inst_dict['status']]
+ inst_dict['addresses'] = _addresses_builder(req)(inst)
+
+ # Return the metadata as a dictionary
+ metadata = {}
+ for item in inst['metadata']:
+ metadata[item['key']] = item['value']
+ inst_dict['metadata'] = metadata
+
+ inst_dict['hostId'] = ''
+ if inst['host']:
+ inst_dict['hostId'] = hashlib.sha224(inst['host']).hexdigest()
+
+ return dict(server=inst_dict)
+
+
+def _addresses_builder(req):
+ version = req.environ['nova.context'].version
+ if version == '1.1':
+ return _build_addresses_11
+ else:
+ return _build_addresses_10
+
+
+def _build_addresses_10(inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ return dict(public=public_ips, private=private_ips)
+
+
+def _build_addresses_11(inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ private_ips = [dict(version=4, addr=a) for a in private_ips]
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ public_ips = [dict(version=4, addr=a) for a in public_ips]
+ return dict(public=public_ips, private=private_ips)
+
+
--
cgit
From de1197cfee200782a5a1d07fb40138d4f103890e Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Sun, 13 Mar 2011 10:49:56 +0100
Subject: Fix instructions for setting up the initial database.
---
bin/nova-manage | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index a880a9c2f..cb4d18614 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -276,7 +276,7 @@ def _db_error(caught_exception):
print caught_exception
print _("The above error may show that the database has not "
"been created.\nPlease create a database using "
- "nova-manage sync db before running this command.")
+ "'nova-manage db sync' before running this command.")
exit(1)
--
cgit
From 9164b8d224ae6629cdac00248b98fad762bdfc10 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Mon, 14 Mar 2011 10:46:26 +0100
Subject: Make utils.execute not overwrite std{in,out,err} args to Popen on
retries. Make utils.execute reject unknown kwargs.
Add a couple of unit tests for utils.execute.
---
nova/tests/test_utils.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++++
nova/utils.py | 25 +++++++++-------
2 files changed, 91 insertions(+), 10 deletions(-)
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py
index 34a407f1a..51cd57c76 100644
--- a/nova/tests/test_utils.py
+++ b/nova/tests/test_utils.py
@@ -14,11 +14,87 @@
# License for the specific language governing permissions and limitations
# under the License.
+import os
+import tempfile
+
from nova import test
from nova import utils
from nova import exception
+class ExecuteTestCase(test.TestCase):
+ def test_retry_on_failure(self):
+ fd, tmpfilename = tempfile.mkstemp()
+ _, tmpfilename2 = tempfile.mkstemp()
+ try:
+ fp = os.fdopen(fd, 'w+')
+ fp.write('''#!/bin/sh
+# If stdin fails to get passed during one of the runs, make a note.
+if ! grep -q foo
+then
+ echo 'failure' > "$1"
+fi
+# If stdin has failed to get passed during this or a previous run, exit early.
+if grep failure "$1"
+then
+ exit 1
+fi
+runs="$(cat $1)"
+if [ -z "$runs" ]
+then
+ runs=0
+fi
+runs=$(($runs + 1))
+echo $runs > "$1"
+exit 1
+''')
+ fp.close()
+ os.chmod(tmpfilename, 0755)
+ self.assertRaises(exception.ProcessExecutionError,
+ utils.execute,
+ tmpfilename, tmpfilename2, attempts=10,
+ process_input='foo',
+ delay_on_retry=False)
+ fp = open(tmpfilename2, 'r+')
+ runs = fp.read()
+ fp.close()
+ self.assertNotEquals(runs.strip(), 'failure', 'stdin did not '
+ 'always get passed '
+ 'correctly')
+ runs = int(runs.strip())
+ self.assertEquals(runs, 10, 'Ran %d times instead of 10.' % (runs,))
+ finally:
+ os.unlink(tmpfilename)
+ os.unlink(tmpfilename2)
+
+ def test_unknown_kwargs_raises_error(self):
+ self.assertRaises(exception.Error,
+ utils.execute,
+ '/bin/true', this_is_not_a_valid_kwarg=True)
+
+ def test_no_retry_on_success(self):
+ fd, tmpfilename = tempfile.mkstemp()
+ _, tmpfilename2 = tempfile.mkstemp()
+ try:
+ fp = os.fdopen(fd, 'w+')
+ fp.write('''#!/bin/sh
+# If we've already run, bail out.
+grep -q foo "$1" && exit 1
+# Mark that we've run before.
+echo foo > "$1"
+# Check that stdin gets passed correctly.
+grep foo
+''')
+ fp.close()
+ os.chmod(tmpfilename, 0755)
+ utils.execute(tmpfilename,
+ tmpfilename2,
+ process_input='foo',
+ attempts=2)
+ finally:
+ os.unlink(tmpfilename)
+ os.unlink(tmpfilename2)
+
class GetFromPathTestCase(test.TestCase):
def test_tolerates_nones(self):
f = utils.get_from_path
diff --git a/nova/utils.py b/nova/utils.py
index 87e726394..2a98411ea 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -133,13 +133,14 @@ def fetchfile(url, target):
def execute(*cmd, **kwargs):
- process_input = kwargs.get('process_input', None)
- addl_env = kwargs.get('addl_env', None)
- check_exit_code = kwargs.get('check_exit_code', 0)
- stdin = kwargs.get('stdin', subprocess.PIPE)
- stdout = kwargs.get('stdout', subprocess.PIPE)
- stderr = kwargs.get('stderr', subprocess.PIPE)
- attempts = kwargs.get('attempts', 1)
+ process_input = kwargs.pop('process_input', None)
+ addl_env = kwargs.pop('addl_env', None)
+ check_exit_code = kwargs.pop('check_exit_code', 0)
+ delay_on_retry = kwargs.pop('delay_on_retry', True)
+ attempts = kwargs.pop('attempts', 1)
+ if len(kwargs):
+ raise exception.Error(_('Got unknown keyword args '
+ 'to utils.execute: %r') % kwargs)
cmd = map(str, cmd)
while attempts > 0:
@@ -149,8 +150,11 @@ def execute(*cmd, **kwargs):
env = os.environ.copy()
if addl_env:
env.update(addl_env)
- obj = subprocess.Popen(cmd, stdin=stdin,
- stdout=stdout, stderr=stderr, env=env)
+ obj = subprocess.Popen(cmd,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env)
result = None
if process_input != None:
result = obj.communicate(process_input)
@@ -176,7 +180,8 @@ def execute(*cmd, **kwargs):
raise
else:
LOG.debug(_("%r failed. Retrying."), cmd)
- greenthread.sleep(random.randint(20, 200) / 100.0)
+ if delay_on_retry:
+ greenthread.sleep(random.randint(20, 200) / 100.0)
def ssh_execute(ssh, cmd, process_input=None,
--
cgit
From c8fc7ed48be84e3b39ab88c8c103fbe52b6718e1 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Mon, 14 Mar 2011 14:06:10 +0100
Subject: Add a unit test
---
nova/network/linux_net.py | 17 +++++++++++------
nova/tests/test_network.py | 27 +++++++++++++++++++++++++++
2 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 0bcc36081..f55662a7a 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -18,7 +18,7 @@ Implements vlans, bridges, and iptables rules using linux utilities.
"""
import os
-import time
+import calendar
from nova import db
from nova import exception
@@ -380,12 +380,17 @@ interface %s
def _host_lease(fixed_ip_ref):
"""Return a host string for an address in leasefile format"""
instance_ref = fixed_ip_ref['instance']
- timestamp = time.mktime(instance_ref['updated_at'].timetuple())
+ if instance_ref['updated_at']:
+ timestamp = instance_ref['updated_at']
+ else:
+ timestamp = instance_ref['created_at']
+
+ seconds_since_epoch = calendar.timegm(timestamp.utctimetuple())
- return "%d %s %s %s" % (timestamp + FLAGS.dhcp_lease_time,
- instance_ref['mac_address'],
- instance_ref['hostname'],
- fixed_ip_ref['address'])
+ return "%d %s %s %s *" % (seconds_since_epoch + FLAGS.dhcp_lease_time,
+ instance_ref['mac_address'],
+ fixed_ip_ref['address'],
+ instance_ref['hostname'] or '*')
def _host_dhcp(fixed_ip_ref):
diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py
index ce1c77210..b7a76be85 100644
--- a/nova/tests/test_network.py
+++ b/nova/tests/test_network.py
@@ -20,6 +20,7 @@ Unit Tests for network code
"""
import IPy
import os
+import time
from nova import context
from nova import db
@@ -29,6 +30,7 @@ from nova import log as logging
from nova import test
from nova import utils
from nova.auth import manager
+from nova.network import linux_net
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.tests.network')
@@ -321,6 +323,31 @@ class NetworkTestCase(test.TestCase):
network['id'])
self.assertEqual(ip_count, num_available_ips)
+ def test_dhcp_lease_output(self):
+ admin_ctxt = context.get_admin_context()
+ address = self._create_address(0, self.instance_id)
+ lease_ip(address)
+ network_ref = db.network_get_by_instance(admin_ctxt, self.instance_id)
+ leases = linux_net.get_dhcp_leases(context.get_admin_context(),
+ network_ref['id'])
+ for line in leases.split('\n'):
+ seconds, mac, ip, hostname, client_id = line.split(' ')
+ self.assertTrue(int(seconds) > time.time(), 'Lease expires in '
+ 'the past')
+ octets = mac.split(':')
+ self.assertEqual(len(octets), 6, "Wrong number of octets "
+ "in %s" % (max,))
+ for octet in octets:
+ self.assertEqual(len(octet), 2, "Oddly sized octet: %s"
+ % (octet,))
+ # This will throw an exception if the octet is invalid
+ int(octet, 16)
+
+ # And this will raise an exception in case of an invalid IP
+ IPy.IP(ip)
+
+ release_ip(address)
+
def is_allocated_in_project(address, project_id):
"""Returns true if address is in specified project"""
--
cgit
From d45947d83fa22f98b0889d269d447fabaa764a3c Mon Sep 17 00:00:00 2001
From: Anne Gentle
Date: Mon, 14 Mar 2011 09:48:58 -0500
Subject: Fixes link to 2011.1 instad of just to trunk docs
---
doc/source/_theme/layout.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/source/_theme/layout.html b/doc/source/_theme/layout.html
index 958c512e4..0a37a7943 100644
--- a/doc/source/_theme/layout.html
+++ b/doc/source/_theme/layout.html
@@ -73,7 +73,7 @@
- Psst... hey. You're reading the latest content, but it might be out of sync with code. You can read Nova 2011.1 docs or all OpenStack docs too.
+ Psst... hey. You're reading the latest content, but it might be out of sync with code. You can read Nova 2011.1 docs or all OpenStack docs too.
{%- endif %}
--
cgit
From 06051ac8660983aae9ea6e72ab9bb1a31ceed9af Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Mon, 14 Mar 2011 11:08:00 -0700
Subject: Reverted unmodified files
---
tools/ajaxterm/README.txt | 240 +++++++++++++++++++++++-----------------------
1 file changed, 120 insertions(+), 120 deletions(-)
diff --git a/tools/ajaxterm/README.txt b/tools/ajaxterm/README.txt
index a649771c5..4b0ae99af 100644
--- a/tools/ajaxterm/README.txt
+++ b/tools/ajaxterm/README.txt
@@ -1,120 +1,120 @@
-= [http://antony.lesuisse.org/qweb/trac/wiki/AjaxTerm Ajaxterm] =
-
-Ajaxterm is a web based terminal. It was totally inspired and works almost
-exactly like http://anyterm.org/ except it's much easier to install (see
-comparaison with anyterm below).
-
-Ajaxterm written in python (and some AJAX javascript for client side) and depends only on python2.3 or better.[[BR]]
-Ajaxterm is '''very simple to install''' on Linux, MacOS X, FreeBSD, Solaris, cygwin and any Unix that runs python2.3.[[BR]]
-Ajaxterm was written by Antony Lesuisse (email: al AT udev.org), License Public Domain.
-
-Use the [/qweb/forum/viewforum.php?id=2 Forum], if you have any question or remark.
-
-== News ==
-
- * 2006-10-29: v0.10 allow space in login, cgi launch fix, redhat init
- * 2006-07-12: v0.9 change uid, daemon fix (Daniel Fischer)
- * 2006-07-04: v0.8 add login support to ssh (Sven Geggus), change max width to 256
- * 2006-05-31: v0.7 minor fixes, daemon option
- * 2006-05-23: v0.6 Applied debian and gentoo patches, renamed to Ajaxterm, default port 8022
-
-== Download and Install ==
-
- * Release: [/qweb/files/Ajaxterm-0.10.tar.gz Ajaxterm-0.10.tar.gz]
- * Browse src: [/qweb/trac/browser/trunk/ajaxterm/ ajaxterm/]
-
-To install Ajaxterm issue the following commands:
-{{{
-wget http://antony.lesuisse.org/qweb/files/Ajaxterm-0.10.tar.gz
-tar zxvf Ajaxterm-0.10.tar.gz
-cd Ajaxterm-0.10
-./ajaxterm.py
-}}}
-Then point your browser to this URL : http://localhost:8022/
-
-== Screenshot ==
-
-{{{
-#!html
-
-}}}
-
-== Documentation and Caveats ==
-
- * Ajaxterm only support latin1, if you use Ubuntu or any LANG==en_US.UTF-8 distribution don't forget to "unset LANG".
-
- * If run as root ajaxterm will run /bin/login, otherwise it will run ssh
- localhost. To use an other command use the -c option.
-
- * By default Ajaxterm only listen at 127.0.0.1:8022. For remote access, it is
- strongly recommended to use '''https SSL/TLS''', and that is simple to
- configure if you use the apache web server using mod_proxy.[[BR]][[BR]]
- Using ssl will also speed up ajaxterm (probably because of keepalive).[[BR]][[BR]]
- Here is an configuration example:
-
-{{{
- Listen 443
- NameVirtualHost *:443
-
-
- ServerName localhost
- SSLEngine On
- SSLCertificateKeyFile ssl/apache.pem
- SSLCertificateFile ssl/apache.pem
-
- ProxyRequests Off
-
- Order deny,allow
- Allow from all
-
- ProxyPass /ajaxterm/ http://localhost:8022/
- ProxyPassReverse /ajaxterm/ http://localhost:8022/
-
-}}}
-
- * Using GET HTTP request seems to speed up ajaxterm, just click on GET in the
- interface, but be warned that your keystrokes might be loggued (by apache or
- any proxy). I usually enable it after the login.
-
- * Ajaxterm commandline usage:
-
-{{{
-usage: ajaxterm.py [options]
-
-options:
- -h, --help show this help message and exit
- -pPORT, --port=PORT Set the TCP port (default: 8022)
- -cCMD, --command=CMD set the command (default: /bin/login or ssh localhost)
- -l, --log log requests to stderr (default: quiet mode)
- -d, --daemon run as daemon in the background
- -PPIDFILE, --pidfile=PIDFILE
- set the pidfile (default: /var/run/ajaxterm.pid)
- -iINDEX_FILE, --index=INDEX_FILE
- default index file (default: ajaxterm.html)
- -uUID, --uid=UID Set the daemon's user id
-}}}
-
- * Ajaxterm was first written as a demo for qweb (my web framework), but
- actually doesn't use many features of qweb.
-
- * Compared to anyterm:
- * There are no partial updates, ajaxterm updates either all the screen or
- nothing. That make the code simpler and I also think it's faster. HTTP
- replies are always gzencoded. When used in 80x25 mode, almost all of
- them are below the 1500 bytes (size of an ethernet frame) and we just
- replace the screen with the reply (no javascript string handling).
- * Ajaxterm polls the server for updates with an exponentially growing
- timeout when the screen hasn't changed. The timeout is also resetted as
- soon as a key is pressed. Anyterm blocks on a pending request and use a
- parallel connection for keypresses. The anyterm approch is better
- when there aren't any keypress.
-
- * Ajaxterm files are released in the Public Domain, (except [http://sarissa.sourceforge.net/doc/ sarissa*] which are LGPL).
-
-== TODO ==
-
- * insert mode ESC [ 4 h
- * change size x,y from gui (sending signal)
- * vt102 graphic codepage
- * use innerHTML or prototype instead of sarissa
-
+= [http://antony.lesuisse.org/qweb/trac/wiki/AjaxTerm Ajaxterm] =
+
+Ajaxterm is a web based terminal. It was totally inspired and works almost
+exactly like http://anyterm.org/ except it's much easier to install (see
+comparaison with anyterm below).
+
+Ajaxterm written in python (and some AJAX javascript for client side) and depends only on python2.3 or better.[[BR]]
+Ajaxterm is '''very simple to install''' on Linux, MacOS X, FreeBSD, Solaris, cygwin and any Unix that runs python2.3.[[BR]]
+Ajaxterm was written by Antony Lesuisse (email: al AT udev.org), License Public Domain.
+
+Use the [/qweb/forum/viewforum.php?id=2 Forum], if you have any question or remark.
+
+== News ==
+
+ * 2006-10-29: v0.10 allow space in login, cgi launch fix, redhat init
+ * 2006-07-12: v0.9 change uid, daemon fix (Daniel Fischer)
+ * 2006-07-04: v0.8 add login support to ssh (Sven Geggus), change max width to 256
+ * 2006-05-31: v0.7 minor fixes, daemon option
+ * 2006-05-23: v0.6 Applied debian and gentoo patches, renamed to Ajaxterm, default port 8022
+
+== Download and Install ==
+
+ * Release: [/qweb/files/Ajaxterm-0.10.tar.gz Ajaxterm-0.10.tar.gz]
+ * Browse src: [/qweb/trac/browser/trunk/ajaxterm/ ajaxterm/]
+
+To install Ajaxterm issue the following commands:
+{{{
+wget http://antony.lesuisse.org/qweb/files/Ajaxterm-0.10.tar.gz
+tar zxvf Ajaxterm-0.10.tar.gz
+cd Ajaxterm-0.10
+./ajaxterm.py
+}}}
+Then point your browser to this URL : http://localhost:8022/
+
+== Screenshot ==
+
+{{{
+#!html
+
+}}}
+
+== Documentation and Caveats ==
+
+ * Ajaxterm only support latin1, if you use Ubuntu or any LANG==en_US.UTF-8 distribution don't forget to "unset LANG".
+
+ * If run as root ajaxterm will run /bin/login, otherwise it will run ssh
+ localhost. To use an other command use the -c option.
+
+ * By default Ajaxterm only listen at 127.0.0.1:8022. For remote access, it is
+ strongly recommended to use '''https SSL/TLS''', and that is simple to
+ configure if you use the apache web server using mod_proxy.[[BR]][[BR]]
+ Using ssl will also speed up ajaxterm (probably because of keepalive).[[BR]][[BR]]
+ Here is an configuration example:
+
+{{{
+ Listen 443
+ NameVirtualHost *:443
+
+
+ ServerName localhost
+ SSLEngine On
+ SSLCertificateKeyFile ssl/apache.pem
+ SSLCertificateFile ssl/apache.pem
+
+ ProxyRequests Off
+
+ Order deny,allow
+ Allow from all
+
+ ProxyPass /ajaxterm/ http://localhost:8022/
+ ProxyPassReverse /ajaxterm/ http://localhost:8022/
+
+}}}
+
+ * Using GET HTTP request seems to speed up ajaxterm, just click on GET in the
+ interface, but be warned that your keystrokes might be loggued (by apache or
+ any proxy). I usually enable it after the login.
+
+ * Ajaxterm commandline usage:
+
+{{{
+usage: ajaxterm.py [options]
+
+options:
+ -h, --help show this help message and exit
+ -pPORT, --port=PORT Set the TCP port (default: 8022)
+ -cCMD, --command=CMD set the command (default: /bin/login or ssh localhost)
+ -l, --log log requests to stderr (default: quiet mode)
+ -d, --daemon run as daemon in the background
+ -PPIDFILE, --pidfile=PIDFILE
+ set the pidfile (default: /var/run/ajaxterm.pid)
+ -iINDEX_FILE, --index=INDEX_FILE
+ default index file (default: ajaxterm.html)
+ -uUID, --uid=UID Set the daemon's user id
+}}}
+
+ * Ajaxterm was first written as a demo for qweb (my web framework), but
+ actually doesn't use many features of qweb.
+
+ * Compared to anyterm:
+ * There are no partial updates, ajaxterm updates either all the screen or
+ nothing. That make the code simpler and I also think it's faster. HTTP
+ replies are always gzencoded. When used in 80x25 mode, almost all of
+ them are below the 1500 bytes (size of an ethernet frame) and we just
+ replace the screen with the reply (no javascript string handling).
+ * Ajaxterm polls the server for updates with an exponentially growing
+ timeout when the screen hasn't changed. The timeout is also resetted as
+ soon as a key is pressed. Anyterm blocks on a pending request and use a
+ parallel connection for keypresses. The anyterm approch is better
+ when there aren't any keypress.
+
+ * Ajaxterm files are released in the Public Domain, (except [http://sarissa.sourceforge.net/doc/ sarissa*] which are LGPL).
+
+== TODO ==
+
+ * insert mode ESC [ 4 h
+ * change size x,y from gui (sending signal)
+ * vt102 graphic codepage
+ * use innerHTML or prototype instead of sarissa
+
--
cgit
--
cgit
From 62d7d521273e19d8e700ab301be38830576efa3b Mon Sep 17 00:00:00 2001
From: Ken Pepple
Date: Mon, 14 Mar 2011 11:27:21 -0700
Subject: small typo in nova-manage vm live-migration
---
bin/nova-manage | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 1eb4e5418..2b42dfff5 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -572,7 +572,7 @@ class VmCommands(object):
"""
ctxt = context.get_admin_context()
- instance_id = ec2_id_to_id(ec2_id)
+ instance_id = ec2utils.ec2_id_to_id(ec2_id)
if FLAGS.connection_type != 'libvirt':
msg = _('Only KVM is supported for now. Sorry!')
--
cgit
From 266ea0bdd1da014a3cf23c7003f7fc932f447d35 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Mon, 14 Mar 2011 15:02:59 -0400
Subject: Create v1_0 and v1_1 packages for the openstack api. Added a servers
module to each. Added tests to validate the structure of ip addresses for a
1.1 request.
---
nova/api/openstack/servers.py | 28 +++++++---------------------
nova/api/openstack/v1_0/__init__.py | 0
nova/api/openstack/v1_0/servers.py | 6 ++++++
nova/api/openstack/v1_1/__init__.py | 0
nova/api/openstack/v1_1/servers.py | 8 ++++++++
nova/tests/api/openstack/fakes.py | 1 +
nova/tests/api/openstack/test_servers.py | 23 +++++++++++++++++++++++
7 files changed, 45 insertions(+), 21 deletions(-)
create mode 100644 nova/api/openstack/v1_0/__init__.py
create mode 100644 nova/api/openstack/v1_0/servers.py
create mode 100644 nova/api/openstack/v1_1/__init__.py
create mode 100644 nova/api/openstack/v1_1/servers.py
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 940c2c47e..0d36546d7 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -27,6 +27,8 @@ from nova import wsgi
from nova import utils
from nova.api.openstack import common
from nova.api.openstack import faults
+from nova.api.openstack.v1_0 import servers as v1_0
+from nova.api.openstack.v1_1 import servers as v1_1
from nova.auth import manager as auth_manager
from nova.compute import instance_types
from nova.compute import power_state
@@ -54,7 +56,7 @@ class Controller(wsgi.Controller):
def ips(self, req, id):
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- return _addresses_builder(req)(instance)
+ return _get_addresses_builder(req)(instance)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -460,7 +462,7 @@ def _build_server(req, inst, is_detail):
inst_dict[k] = inst[v]
inst_dict['status'] = power_mapping[inst_dict['status']]
- inst_dict['addresses'] = _addresses_builder(req)(inst)
+ inst_dict['addresses'] = _get_addresses_builder(req)(inst)
# Return the metadata as a dictionary
metadata = {}
@@ -475,25 +477,9 @@ def _build_server(req, inst, is_detail):
return dict(server=inst_dict)
-def _addresses_builder(req):
+def _get_addresses_builder(req):
version = req.environ['nova.context'].version
if version == '1.1':
- return _build_addresses_11
+ return v1_1.build_addresses
else:
- return _build_addresses_10
-
-
-def _build_addresses_10(inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- return dict(public=public_ips, private=private_ips)
-
-
-def _build_addresses_11(inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- private_ips = [dict(version=4, addr=a) for a in private_ips]
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- public_ips = [dict(version=4, addr=a) for a in public_ips]
- return dict(public=public_ips, private=private_ips)
-
-
+ return v1_0.build_addresses
diff --git a/nova/api/openstack/v1_0/__init__.py b/nova/api/openstack/v1_0/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/nova/api/openstack/v1_0/servers.py b/nova/api/openstack/v1_0/servers.py
new file mode 100644
index 000000000..d332b1378
--- /dev/null
+++ b/nova/api/openstack/v1_0/servers.py
@@ -0,0 +1,6 @@
+from nova import utils
+
+def build_addresses(inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ return dict(public=public_ips, private=private_ips)
diff --git a/nova/api/openstack/v1_1/__init__.py b/nova/api/openstack/v1_1/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/nova/api/openstack/v1_1/servers.py b/nova/api/openstack/v1_1/servers.py
new file mode 100644
index 000000000..012fc3e5c
--- /dev/null
+++ b/nova/api/openstack/v1_1/servers.py
@@ -0,0 +1,8 @@
+from nova import utils
+
+def build_addresses(inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ private_ips = [dict(version=4, addr=a) for a in private_ips]
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ public_ips = [dict(version=4, addr=a) for a in public_ips]
+ return dict(public=public_ips, private=private_ips)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 8ec1629f4..7af62c57f 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -81,6 +81,7 @@ def wsgi_app(inner_application=None):
api = openstack.FaultWrapper(auth.AuthMiddleware(
ratelimiting.RateLimitingMiddleware(inner_application)))
mapper['/v1.0'] = api
+ mapper['/v1.1'] = api
mapper['/'] = openstack.FaultWrapper(openstack.Versions())
return mapper
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index c1e05b18a..a2bd875a4 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -21,6 +21,7 @@ import json
import stubout
import webob
+from nova import context
from nova import db
from nova import flags
from nova import test
@@ -176,6 +177,28 @@ class ServersTest(test.TestCase):
self.assertEqual(len(addresses["private"]), 1)
self.assertEqual(addresses["private"][0], private)
+ def test_get_server_by_id_with_addresses_v1_1(self):
+ class FakeRequestContext(object):
+ def __init__(self, user, project, *args, **kwargs):
+ self.user_id = 1
+ self.project_id = 1
+ self.version = '1.1'
+ self.stubs.Set(context, 'RequestContext', FakeRequestContext)
+ private = "192.168.0.3"
+ public = ["1.2.3.4"]
+ new_return_server = return_server_with_addresses(private, public)
+ self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
+ req = webob.Request.blank('/v1.1/servers/1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ self.assertEqual(res_dict['server']['id'], '1')
+ self.assertEqual(res_dict['server']['name'], 'server1')
+ addresses = res_dict['server']['addresses']
+ self.assertEqual(len(addresses["public"]), len(public))
+ self.assertEqual(addresses["public"][0], {"version": 4, "addr": public[0]})
+ self.assertEqual(len(addresses["private"]), 1)
+ self.assertEqual(addresses["private"][0], {"version": 4, "addr": private})
+
def test_get_server_list(self):
req = webob.Request.blank('/v1.0/servers')
res = req.get_response(fakes.wsgi_app())
--
cgit
--
cgit
From f4e7da2d9f7d6793b383c5f187939f19ec849f0a Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Mon, 14 Mar 2011 21:10:11 +0100
Subject: Include cpuinfo.xml.template in tarball.
---
MANIFEST.in | 1 +
1 file changed, 1 insertion(+)
diff --git a/MANIFEST.in b/MANIFEST.in
index 2ceed34f3..bf30d1546 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -25,6 +25,7 @@ include nova/db/sqlalchemy/migrate_repo/migrate.cfg
include nova/db/sqlalchemy/migrate_repo/README
include nova/virt/interfaces.template
include nova/virt/libvirt*.xml.template
+include nova/virt/cpuinfo.xml.template
include nova/tests/CA/
include nova/tests/CA/cacert.pem
include nova/tests/CA/private/
--
cgit
--
cgit
From ce57cff740bc7f821bdbb0dd1367c037b6fa1c01 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 14:02:27 -0700
Subject: The exception is called "ApiError", not "APIError"
---
nova/virt/libvirt_conn.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 0b306c950..7994e9547 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -362,19 +362,19 @@ class LibvirtConnection(object):
@exception.wrap_exception
def pause(self, instance, callback):
- raise exception.APIError("pause not supported for libvirt.")
+ raise exception.ApiError("pause not supported for libvirt.")
@exception.wrap_exception
def unpause(self, instance, callback):
- raise exception.APIError("unpause not supported for libvirt.")
+ raise exception.ApiError("unpause not supported for libvirt.")
@exception.wrap_exception
def suspend(self, instance, callback):
- raise exception.APIError("suspend not supported for libvirt")
+ raise exception.ApiError("suspend not supported for libvirt")
@exception.wrap_exception
def resume(self, instance, callback):
- raise exception.APIError("resume not supported for libvirt")
+ raise exception.ApiError("resume not supported for libvirt")
@exception.wrap_exception
def rescue(self, instance, callback=None):
@@ -779,7 +779,7 @@ class LibvirtConnection(object):
'cpu_time': cpu_time}
def get_diagnostics(self, instance_name):
- raise exception.APIError(_("diagnostics are not supported "
+ raise exception.ApiError(_("diagnostics are not supported "
"for libvirt"))
def get_disks(self, instance_name):
--
cgit
From 337bda95a9e12d395f838e81e279c875b056aba9 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Mon, 14 Mar 2011 22:17:14 +0100
Subject: Add missing fallback chain for ipv6.
---
nova/virt/libvirt_conn.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 0b306c950..03f046cbd 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1597,6 +1597,9 @@ class IptablesFirewallDriver(FirewallDriver):
self.iptables.ipv4['filter'].add_chain('sg-fallback')
self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP')
+ if FLAGS.use_ipv6:
+ self.iptables.ipv6['filter'].add_chain('sg-fallback')
+ self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP')
def setup_basic_filtering(self, instance):
"""Use NWFilter from libvirt for this."""
--
cgit
--
cgit
From 408a2591d60f5d238e60e4be9197ccc7262f2406 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Mon, 14 Mar 2011 16:21:33 -0500
Subject: PEP8 cleanup
---
nova/tests/db/fakes.py | 4 ++--
nova/tests/test_xenapi.py | 11 +++++------
nova/virt/xenapi/vm_utils.py | 3 +--
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py
index 142f6b1c6..5e9a3aa3b 100644
--- a/nova/tests/db/fakes.py
+++ b/nova/tests/db/fakes.py
@@ -77,8 +77,8 @@ def stub_out_db_instance_api(stubs):
'mac_address': values['mac_address'],
'vcpus': type_data['vcpus'],
'local_gb': type_data['local_gb'],
- 'os_type': values['os_type']
- }
+ 'os_type': values['os_type']}
+
return FakeModel(base_options)
def fake_network_get_by_instance(context, instance_id):
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index cd125a301..8b0affd5c 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -78,8 +78,7 @@ class XenAPIVolumeTestCase(test.TestCase):
'ramdisk_id': 3,
'instance_type': 'm1.large',
'mac_address': 'aa:bb:cc:dd:ee:ff',
- 'os_type': 'linux'
- }
+ 'os_type': 'linux'}
def _create_volume(self, size='0'):
"""Create a volume object."""
@@ -315,8 +314,8 @@ class XenAPIVMTestCase(test.TestCase):
'ramdisk_id': ramdisk_id,
'instance_type': instance_type,
'mac_address': 'aa:bb:cc:dd:ee:ff',
- 'os_type': os_type
- }
+ 'os_type': os_type}
+
conn = xenapi_conn.get_connection(False)
instance = db.instance_create(values)
conn.spawn(instance)
@@ -440,8 +439,8 @@ class XenAPIMigrateInstance(test.TestCase):
'ramdisk_id': None,
'instance_type': 'm1.large',
'mac_address': 'aa:bb:cc:dd:ee:ff',
- 'os_type': 'linux'
- }
+ 'os_type': 'linux'}
+
stubs.stub_out_migration_methods(self.stubs)
glance_stubs.stubout_glance_client(self.stubs,
glance_stubs.FakeGlance)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index f07b57796..763c5fe40 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -136,8 +136,7 @@ class VMHelper(HelperBase):
'VCPUs_at_startup': vcpus,
'VCPUs_max': vcpus,
'VCPUs_params': {},
- 'xenstore_data': {}
- }
+ 'xenstore_data': {}}
# Complete VM configuration record according to the image type
# non-raw/raw with PV kernel/raw in HVM mode
--
cgit
From 7c6aa5eb302637dde0d800f7155235858bbabbeb Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 14:32:15 -0700
Subject: Removed duplicated test, renamed same-named (but non-identical) tests
---
nova/tests/test_compute.py | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index e486050be..14559d1dc 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -115,7 +115,7 @@ class ComputeTestCase(test.TestCase):
finally:
db.instance_destroy(self.context, ref[0]['id'])
- def test_create_instance_associates_security_groups(self):
+ def test_create_instance_associates_security_groups_1(self):
"""Make sure create associates security groups"""
group = self._create_group()
instance_ref = models.Instance()
@@ -124,18 +124,7 @@ class ComputeTestCase(test.TestCase):
instance_ref['hostname'] = 'i-00000001'
return instance_ref
- def test_create_instance_defaults_display_name(self):
- """Verify that an instance cannot be created without a display_name."""
- cases = [dict(), dict(display_name=None)]
- for instance in cases:
- ref = self.compute_api.create(self.context,
- FLAGS.default_instance_type, None, **instance)
- try:
- self.assertNotEqual(ref[0]['display_name'], None)
- finally:
- db.instance_destroy(self.context, ref[0]['id'])
-
- def test_create_instance_associates_security_groups(self):
+ def test_create_instance_associates_security_groups_2(self):
"""Make sure create associates security groups"""
group = self._create_group()
ref = self.compute_api.create(
--
cgit
From 5a0d4fbc24e897e4aa861819fd2f861e7dedcb6b Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Mon, 14 Mar 2011 16:33:01 -0500
Subject: added structure to virt.xenapi.vmops to support network info being
passed in
---
nova/virt/xenapi/vmops.py | 109 ++++++++++++++++++++++++++--------------------
1 file changed, 62 insertions(+), 47 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index fcb290d03..bec403543 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -80,11 +80,11 @@ class VMOps(object):
instance.image_id, user, project, disk_image_type)
return vdi_uuid
- def spawn(self, instance):
+ def spawn(self, instance, network_info=None):
vdi_uuid = self.create_disk(instance)
- self._spawn_with_disk(instance, vdi_uuid=vdi_uuid)
+ self._spawn_with_disk(instance, vdi_uuid=vdi_uuid, network_info)
- def _spawn_with_disk(self, instance, vdi_uuid):
+ def _spawn_with_disk(self, instance, vdi_uuid, network_info=None):
"""Create VM instance"""
instance_name = instance.name
vm_ref = VMHelper.lookup(self._session, instance_name)
@@ -128,8 +128,13 @@ class VMOps(object):
vdi_ref=vdi_ref, userdevice=0, bootable=True)
# inject_network_info and create vifs
- networks = self.inject_network_info(instance)
- self.create_vifs(instance, networks)
+ if network_info is not None:
+ self.inject_network_info(instance, network_info)
+ self.create_vifs(instance, [nw for (nw, mapping) in network_info])
+ else:
+ # TODO(tr3buchet) - goes away with multi-nic
+ networks = self.inject_network_info(instance)
+ self.create_vifs(instance, networks)
LOG.debug(_('Starting VM %s...'), vm_ref)
self._start(instance, vm_ref)
@@ -684,59 +689,68 @@ class VMOps(object):
# TODO: implement this!
return 'http://fakeajaxconsole/fake_url'
- def inject_network_info(self, instance):
+ def inject_network_info(self, instance, network_info=None):
"""
Generate the network info and make calls to place it into the
xenstore and the xenstore param list
"""
- # TODO(tr3buchet) - remove comment in multi-nic
- # I've decided to go ahead and consider multiple IPs and networks
- # at this stage even though they aren't implemented because these will
- # be needed for multi-nic and there was no sense writing it for single
- # network/single IP and then having to turn around and re-write it
vm_ref = self._get_vm_opaque_ref(instance.id)
logging.debug(_("injecting network info to xenstore for vm: |%s|"),
vm_ref)
- admin_context = context.get_admin_context()
- IPs = db.fixed_ip_get_all_by_instance(admin_context, instance['id'])
- networks = db.network_get_all_by_instance(admin_context,
+ if network_info is not None:
+ for (network, mapping) in network_info:
+ self.write_to_param_xenstore(vm_ref, {location: mapping})
+ try:
+ self.write_to_xenstore(vm_ref, location,
+ mapping['location'])
+ except KeyError:
+ # catch KeyError for domid if instance isn't running
+ pass
+ else:
+ # TODO(tr3buchet) - this bit here when network_info is None goes
+ # away with multi-nic
+ admin_context = context.get_admin_context()
+ IPs = db.fixed_ip_get_all_by_instance(admin_context,
instance['id'])
- for network in networks:
- network_IPs = [ip for ip in IPs if ip.network_id == network.id]
-
- def ip_dict(ip):
- return {
- "ip": ip.address,
- "netmask": network["netmask"],
- "enabled": "1"}
-
- def ip6_dict(ip6):
- return {
- "ip": ip6.addressV6,
- "netmask": ip6.netmaskV6,
- "gateway": ip6.gatewayV6,
- "enabled": "1"}
-
- mac_id = instance.mac_address.replace(':', '')
- location = 'vm-data/networking/%s' % mac_id
- mapping = {
- 'label': network['label'],
- 'gateway': network['gateway'],
- 'mac': instance.mac_address,
- 'dns': [network['dns']],
- 'ips': [ip_dict(ip) for ip in network_IPs],
- 'ip6s': [ip6_dict(ip) for ip in network_IPs]}
-
- self.write_to_param_xenstore(vm_ref, {location: mapping})
+ networks = db.network_get_all_by_instance(admin_context,
+ instance['id'])
+ for network in networks:
+ network_IPs = [ip for ip in IPs if ip.network_id == network.id]
+
+ def ip_dict(ip):
+ return {
+ "ip": ip.address,
+ "netmask": network["netmask"],
+ "enabled": "1"}
+
+ def ip6_dict(ip6):
+ return {
+ "ip": ip6.addressV6,
+ "netmask": ip6.netmaskV6,
+ "gateway": ip6.gatewayV6,
+ "enabled": "1"}
+
+ mac_id = instance.mac_address.replace(':', '')
+ location = 'vm-data/networking/%s' % mac_id
+ mapping = {
+ 'label': network['label'],
+ 'gateway': network['gateway'],
+ 'mac': instance.mac_address,
+ 'dns': [network['dns']],
+ 'ips': [ip_dict(ip) for ip in network_IPs],
+ 'ip6s': [ip6_dict(ip) for ip in network_IPs]}
+
+ self.write_to_param_xenstore(vm_ref, {location: mapping})
- try:
- self.write_to_xenstore(vm_ref, location, mapping['location'])
- except KeyError:
- # catch KeyError for domid if instance isn't running
- pass
+ try:
+ self.write_to_xenstore(vm_ref, location,
+ mapping['location'])
+ except KeyError:
+ # catch KeyError for domid if instance isn't running
+ pass
- return networks
+ return networks
def create_vifs(self, instance, networks=None):
"""
@@ -745,6 +759,7 @@ class VMOps(object):
"""
vm_ref = self._get_vm_opaque_ref(instance.id)
logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
+ # TODO(tr3buchet) - goes away with multi-nic
if networks is None:
networks = db.network_get_all_by_instance(admin_context,
instance['id'])
--
cgit
From 1d1e5e38175ff7956b3a28ccc1ce61f700700e8b Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Mon, 14 Mar 2011 16:38:53 -0500
Subject: fixed keyword arg error
---
nova/virt/xenapi/vmops.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index bec403543..64f2c6231 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -82,7 +82,7 @@ class VMOps(object):
def spawn(self, instance, network_info=None):
vdi_uuid = self.create_disk(instance)
- self._spawn_with_disk(instance, vdi_uuid=vdi_uuid, network_info)
+ self._spawn_with_disk(instance, vdi_uuid, network_info)
def _spawn_with_disk(self, instance, vdi_uuid, network_info=None):
"""Create VM instance"""
--
cgit
From 5b1422afe12d4e9b7fdfdc6a61cdcd51962dab4d Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Mon, 14 Mar 2011 14:43:53 -0700
Subject: Fix for LP Bug #704300
---
nova/virt/libvirt_conn.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 0b306c950..e0222956e 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -339,7 +339,11 @@ class LibvirtConnection(object):
def reboot(self, instance):
self.destroy(instance, False)
xml = self.to_xml(instance)
+ self.firewall_driver.setup_basic_filtering(instance)
+ self.firewall_driver.prepare_instance_filter(instance)
self._conn.createXML(xml, 0)
+ self.firewall_driver.apply_instance_filter(instance)
+
timer = utils.LoopingCall(f=None)
def _wait_for_reboot():
--
cgit
From 567e3bc3a7e66896482d83420190a7c4a8df1e5a Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 15:17:00 -0700
Subject: So the first of those tests doesn't pass. Removing as it looks like
it was meant to be deleted.
---
nova/tests/test_compute.py | 11 +----------
1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 14559d1dc..3651f4cef 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -115,16 +115,7 @@ class ComputeTestCase(test.TestCase):
finally:
db.instance_destroy(self.context, ref[0]['id'])
- def test_create_instance_associates_security_groups_1(self):
- """Make sure create associates security groups"""
- group = self._create_group()
- instance_ref = models.Instance()
- instance_ref['id'] = 1
- instance_ref['volumes'] = [{'id': 1}, {'id': 2}]
- instance_ref['hostname'] = 'i-00000001'
- return instance_ref
-
- def test_create_instance_associates_security_groups_2(self):
+ def test_create_instance_associates_security_groups(self):
"""Make sure create associates security groups"""
group = self._create_group()
ref = self.compute_api.create(
--
cgit
From a4e94971b696681a5ced189d8f4263c8f77cc531 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 19:57:30 -0700
Subject: Make key_pair optional with OpenStack API
---
nova/api/openstack/servers.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index dc28a0782..47ed254ec 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -146,10 +146,14 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
context = req.environ['nova.context']
+
+ key_name = None
+ key_data = None
key_pairs = auth_manager.AuthManager.get_key_pairs(context)
- if not key_pairs:
- raise exception.NotFound(_("No keypairs defined"))
- key_pair = key_pairs[0]
+ if key_pairs:
+ key_pair = key_pairs[0]
+ key_name = key_pair['name']
+ key_data = key_pair['public_key']
image_id = common.get_image_id_from_image_hash(self._image_service,
context, env['server']['imageId'])
@@ -174,8 +178,8 @@ class Controller(wsgi.Controller):
ramdisk_id=ramdisk_id,
display_name=env['server']['name'],
display_description=env['server']['name'],
- key_name=key_pair['name'],
- key_data=key_pair['public_key'],
+ key_name=key_name,
+ key_data=key_data,
metadata=metadata,
onset_files=env.get('onset_files', []))
--
cgit
From d5b9391e2911ba2210a045a2af380dfc85d16919 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Mon, 14 Mar 2011 23:14:59 -0400
Subject: Added a views package and a views.servers module. For representing
the response object before it is serialized.
---
nova/api/openstack/servers.py | 65 +++++----------------------
nova/api/openstack/v1_0/__init__.py | 0
nova/api/openstack/v1_0/servers.py | 6 ---
nova/api/openstack/v1_1/__init__.py | 0
nova/api/openstack/v1_1/servers.py | 8 ----
nova/api/openstack/views/__init__.py | 0
nova/api/openstack/views/servers.py | 76 ++++++++++++++++++++++++++++++++
nova/tests/api/openstack/test_servers.py | 6 ++-
8 files changed, 90 insertions(+), 71 deletions(-)
delete mode 100644 nova/api/openstack/v1_0/__init__.py
delete mode 100644 nova/api/openstack/v1_0/servers.py
delete mode 100644 nova/api/openstack/v1_1/__init__.py
delete mode 100644 nova/api/openstack/v1_1/servers.py
create mode 100644 nova/api/openstack/views/__init__.py
create mode 100644 nova/api/openstack/views/servers.py
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 0d36546d7..ea8321e8d 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -27,8 +27,7 @@ from nova import wsgi
from nova import utils
from nova.api.openstack import common
from nova.api.openstack import faults
-from nova.api.openstack.v1_0 import servers as v1_0
-from nova.api.openstack.v1_1 import servers as v1_1
+from nova.api.openstack.views.servers import get_view_builder
from nova.auth import manager as auth_manager
from nova.compute import instance_types
from nova.compute import power_state
@@ -56,7 +55,8 @@ class Controller(wsgi.Controller):
def ips(self, req, id):
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- return _get_addresses_builder(req)(instance)
+ builder = get_view_builder(req)
+ return builder._build_addresses(instance)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -75,15 +75,17 @@ class Controller(wsgi.Controller):
"""
instance_list = self.compute_api.get_all(req.environ['nova.context'])
limited_list = common.limited(instance_list, req)
- res = [_build_server(req, inst, is_detail)['server']
+ builder = get_view_builder(req)
+ servers = [builder.build(inst, is_detail)['server']
for inst in limited_list]
- return dict(servers=res)
+ return dict(servers=servers)
def show(self, req, id):
""" Returns server details by server id """
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- return _build_server(req, instance, is_detail=True)
+ builder = get_view_builder(req)
+ return builder.build(instance, is_detail=True)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -135,7 +137,8 @@ class Controller(wsgi.Controller):
metadata=metadata,
onset_files=env.get('onset_files', []))
- server = _build_server(req, instances[0], is_detail=False)
+ builder = get_view_builder(req)
+ server = builder.build(instances[0], is_detail=False)
password = "%s%s" % (server['server']['name'][:4],
utils.generate_password(12))
server['server']['adminPass'] = password
@@ -434,52 +437,4 @@ class Controller(wsgi.Controller):
return kernel_id, ramdisk_id
-def _build_server(req, inst, is_detail):
- """ Coerces into dictionary format, mapping everything to Rackspace-like
- attributes for return"""
- if not is_detail:
- return dict(server=dict(id=inst['id'], name=inst['display_name']))
-
- power_mapping = {
- None: 'build',
- power_state.NOSTATE: 'build',
- power_state.RUNNING: 'active',
- power_state.BLOCKED: 'active',
- power_state.SUSPENDED: 'suspended',
- power_state.PAUSED: 'paused',
- power_state.SHUTDOWN: 'active',
- power_state.SHUTOFF: 'active',
- power_state.CRASHED: 'error',
- power_state.FAILED: 'error'}
- inst_dict = {}
- version = req.environ['nova.context'].version
-
- mapped_keys = dict(status='state', imageId='image_id',
- flavorId='instance_type', name='display_name', id='id')
-
- for k, v in mapped_keys.iteritems():
- inst_dict[k] = inst[v]
-
- inst_dict['status'] = power_mapping[inst_dict['status']]
- inst_dict['addresses'] = _get_addresses_builder(req)(inst)
-
- # Return the metadata as a dictionary
- metadata = {}
- for item in inst['metadata']:
- metadata[item['key']] = item['value']
- inst_dict['metadata'] = metadata
-
- inst_dict['hostId'] = ''
- if inst['host']:
- inst_dict['hostId'] = hashlib.sha224(inst['host']).hexdigest()
-
- return dict(server=inst_dict)
-
-
-def _get_addresses_builder(req):
- version = req.environ['nova.context'].version
- if version == '1.1':
- return v1_1.build_addresses
- else:
- return v1_0.build_addresses
diff --git a/nova/api/openstack/v1_0/__init__.py b/nova/api/openstack/v1_0/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/nova/api/openstack/v1_0/servers.py b/nova/api/openstack/v1_0/servers.py
deleted file mode 100644
index d332b1378..000000000
--- a/nova/api/openstack/v1_0/servers.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from nova import utils
-
-def build_addresses(inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- return dict(public=public_ips, private=private_ips)
diff --git a/nova/api/openstack/v1_1/__init__.py b/nova/api/openstack/v1_1/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/nova/api/openstack/v1_1/servers.py b/nova/api/openstack/v1_1/servers.py
deleted file mode 100644
index 012fc3e5c..000000000
--- a/nova/api/openstack/v1_1/servers.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from nova import utils
-
-def build_addresses(inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- private_ips = [dict(version=4, addr=a) for a in private_ips]
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- public_ips = [dict(version=4, addr=a) for a in public_ips]
- return dict(public=public_ips, private=private_ips)
diff --git a/nova/api/openstack/views/__init__.py b/nova/api/openstack/views/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
new file mode 100644
index 000000000..120cae4d4
--- /dev/null
+++ b/nova/api/openstack/views/servers.py
@@ -0,0 +1,76 @@
+import hashlib
+from nova.compute import power_state
+from nova import utils
+
+def get_view_builder(req):
+ '''
+ A factory method that returns the correct builder based on the version of
+ the api requested.
+ '''
+ version = req.environ['nova.context'].version
+ if version == '1.1':
+ return DataViewBuilder_1_1()
+ else:
+ return DataViewBuilder_1_0()
+
+
+class DataViewBuilder(object):
+ ''' Models a server response as a python dictionary. '''
+
+ def build(self, inst, is_detail):
+ """ Coerces into dictionary format, mapping everything to Rackspace-like
+ attributes for return"""
+
+ if not is_detail:
+ return dict(server=dict(id=inst['id'], name=inst['display_name']))
+
+ power_mapping = {
+ None: 'build',
+ power_state.NOSTATE: 'build',
+ power_state.RUNNING: 'active',
+ power_state.BLOCKED: 'active',
+ power_state.SUSPENDED: 'suspended',
+ power_state.PAUSED: 'paused',
+ power_state.SHUTDOWN: 'active',
+ power_state.SHUTOFF: 'active',
+ power_state.CRASHED: 'error',
+ power_state.FAILED: 'error'}
+ inst_dict = {}
+
+ mapped_keys = dict(status='state', imageId='image_id',
+ flavorId='instance_type', name='display_name', id='id')
+
+ for k, v in mapped_keys.iteritems():
+ inst_dict[k] = inst[v]
+
+ inst_dict['status'] = power_mapping[inst_dict['status']]
+ inst_dict['addresses'] = self._build_addresses(inst)
+
+ # Return the metadata as a dictionary
+ metadata = {}
+ for item in inst['metadata']:
+ metadata[item['key']] = item['value']
+ inst_dict['metadata'] = metadata
+
+ inst_dict['hostId'] = ''
+ if inst['host']:
+ inst_dict['hostId'] = hashlib.sha224(inst['host']).hexdigest()
+
+ return dict(server=inst_dict)
+
+
+class DataViewBuilder_1_0(DataViewBuilder):
+ def _build_addresses(self, inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ return dict(public=public_ips, private=private_ips)
+
+
+class DataViewBuilder_1_1(DataViewBuilder):
+ def _build_addresses(self, inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ private_ips = [dict(version=4, addr=a) for a in private_ips]
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ public_ips = [dict(version=4, addr=a) for a in public_ips]
+ return dict(public=public_ips, private=private_ips)
+
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index a2bd875a4..b2446f194 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -195,9 +195,11 @@ class ServersTest(test.TestCase):
self.assertEqual(res_dict['server']['name'], 'server1')
addresses = res_dict['server']['addresses']
self.assertEqual(len(addresses["public"]), len(public))
- self.assertEqual(addresses["public"][0], {"version": 4, "addr": public[0]})
+ self.assertEqual(addresses["public"][0],
+ {"version": 4, "addr": public[0]})
self.assertEqual(len(addresses["private"]), 1)
- self.assertEqual(addresses["private"][0], {"version": 4, "addr": private})
+ self.assertEqual(addresses["private"][0],
+ {"version": 4, "addr": private})
def test_get_server_list(self):
req = webob.Request.blank('/v1.0/servers')
--
cgit
From da605eb84f7d5de741225ff936447db01690a04f Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 20:48:33 -0700
Subject: Don't generate insecure passwords where it's easy to use urandom
instead
---
nova/console/manager.py | 2 +-
nova/console/xvp.py | 4 ----
nova/utils.py | 15 ++++++++++-----
3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/nova/console/manager.py b/nova/console/manager.py
index 57c75cf4f..bfa571ea9 100644
--- a/nova/console/manager.py
+++ b/nova/console/manager.py
@@ -69,7 +69,7 @@ class ConsoleProxyManager(manager.Manager):
except exception.NotFound:
logging.debug(_("Adding console"))
if not password:
- password = self.driver.generate_password()
+ password = utils.generate_password(8)
if not port:
port = self.driver.get_port(context)
console_data = {'instance_name': name,
diff --git a/nova/console/xvp.py b/nova/console/xvp.py
index 68d8c8565..0cedfbb13 100644
--- a/nova/console/xvp.py
+++ b/nova/console/xvp.py
@@ -91,10 +91,6 @@ class XVPConsoleProxy(object):
"""Trim password to length, and encode"""
return self._xvp_encrypt(password)
- def generate_password(self, length=8):
- """Returns random console password"""
- return os.urandom(length * 2).encode('base64')[:length]
-
def _rebuild_xvp_conf(self, context):
logging.debug(_("Rebuilding xvp conf"))
pools = [pool for pool in
diff --git a/nova/utils.py b/nova/utils.py
index 87e726394..9c8b27d56 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -263,12 +263,17 @@ def generate_mac():
def generate_password(length=20):
- """Generate a random sequence of letters and digits
- to be used as a password. Note that this is not intended
- to represent the ultimate in security.
+ """Generate a random alphanumeric password, avoiding 'confusing' O,0,I,1.
+
+ Believed to be reasonably secure (with a reasonable password length!)
"""
- chrs = string.letters + string.digits
- return "".join([random.choice(chrs) for i in xrange(length)])
+ # 26 letters, 10 digits = 36
+ # Remove O, 0, I, 1 => 32 digits
+ # 32 digits means we're just using the low 5 bit of each byte
+ chrs = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
+
+ random_bytes = os.urandom(length)
+ return "".join([chrs[ord(random_bytes[i]) % 32] for i in xrange(length)])
def last_octet(address):
--
cgit
From c70e3777a488a63062c030e9949e9c16f2269f9c Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Mon, 14 Mar 2011 23:55:44 -0400
Subject: moving addresses views to new module; removing 'Data' from
'DataViewBuilder'
---
nova/api/openstack/views/servers.py | 39 ++++++++++++++++++++-----------------
1 file changed, 21 insertions(+), 18 deletions(-)
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 120cae4d4..3ccfd8dba 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -1,29 +1,40 @@
import hashlib
from nova.compute import power_state
+from nova.api.openstack.views import addresses as addresses_view
from nova import utils
+
def get_view_builder(req):
'''
A factory method that returns the correct builder based on the version of
the api requested.
'''
version = req.environ['nova.context'].version
+ addresses_builder = addresses_view.get_view_builder(req)
if version == '1.1':
- return DataViewBuilder_1_1()
+ return ViewBuilder_1_1(addresses_builder)
else:
- return DataViewBuilder_1_0()
+ return ViewBuilder_1_0(addresses_builder)
+
+class ViewBuilder(object):
+ ''' Models a server response as a python dictionary.'''
-class DataViewBuilder(object):
- ''' Models a server response as a python dictionary. '''
+ def __init__(self, addresses_builder):
+ self.addresses_builder = addresses_builder
def build(self, inst, is_detail):
""" Coerces into dictionary format, mapping everything to Rackspace-like
attributes for return"""
+ if is_detail:
+ return self._build_detail(inst)
+ else:
+ return self._build_simple(inst)
- if not is_detail:
+ def _build_simple(self, inst):
return dict(server=dict(id=inst['id'], name=inst['display_name']))
+ def _build_detail(self, inst):
power_mapping = {
None: 'build',
power_state.NOSTATE: 'build',
@@ -44,7 +55,7 @@ class DataViewBuilder(object):
inst_dict[k] = inst[v]
inst_dict['status'] = power_mapping[inst_dict['status']]
- inst_dict['addresses'] = self._build_addresses(inst)
+ inst_dict['addresses'] = self.addresses_builder.build(inst)
# Return the metadata as a dictionary
metadata = {}
@@ -59,18 +70,10 @@ class DataViewBuilder(object):
return dict(server=inst_dict)
-class DataViewBuilder_1_0(DataViewBuilder):
- def _build_addresses(self, inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- return dict(public=public_ips, private=private_ips)
+class ViewBuilder_1_0(ViewBuilder):
+ pass
-class DataViewBuilder_1_1(DataViewBuilder):
- def _build_addresses(self, inst):
- private_ips = utils.get_from_path(inst, 'fixed_ip/address')
- private_ips = [dict(version=4, addr=a) for a in private_ips]
- public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
- public_ips = [dict(version=4, addr=a) for a in public_ips]
- return dict(public=public_ips, private=private_ips)
+class ViewBuilder_1_1(ViewBuilder):
+ pass
--
cgit
From 3d0cde272e3227978c5875c811c93e1e3df692ed Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 21:01:48 -0700
Subject: Clarify the logic in using 32 symbols
---
nova/utils.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/nova/utils.py b/nova/utils.py
index 9c8b27d56..0510c3cbe 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -267,9 +267,10 @@ def generate_password(length=20):
Believed to be reasonably secure (with a reasonable password length!)
"""
- # 26 letters, 10 digits = 36
- # Remove O, 0, I, 1 => 32 digits
- # 32 digits means we're just using the low 5 bit of each byte
+ # 26 letters, 10 digits = 36 choices
+ # Remove O, 0, I, 1 => 32 choices
+ # 32 choices means we're just using the low 5 bit of each byte,
+ # so there's no bias introduced by using a modulo
chrs = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
random_bytes = os.urandom(length)
--
cgit
From 3cf224b9e676b88d1990b13476095be6ec156e5d Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 21:28:42 -0700
Subject: Fixed problem with metadata creation (backported fix)
---
nova/db/sqlalchemy/api.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 56998ce05..d4dd82227 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -746,6 +746,15 @@ def instance_create(context, values):
context - request context object
values - dict containing column values.
"""
+ metadata = values.get('metadata')
+ metadata_refs = []
+ if metadata:
+ for metadata_item in metadata:
+ metadata_ref = models.InstanceMetadata()
+ metadata_ref.update(metadata_item)
+ metadata_refs.append(metadata_ref)
+ values['metadata'] = metadata_refs
+
instance_ref = models.Instance()
instance_ref.update(values)
--
cgit
From 18cd549ba8d7aa4c688a7f7a5e940acbaaa03acc Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Tue, 15 Mar 2011 00:49:20 -0400
Subject: adding flavors and images barebones view code; adding flavorRef and
imageRef to v1.1 servers
---
nova/api/openstack/servers.py | 13 +++++-----
nova/api/openstack/views/servers.py | 42 ++++++++++++++++++++++++++++----
nova/tests/api/openstack/test_servers.py | 32 +++++++++++++++++++++---
3 files changed, 72 insertions(+), 15 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index ea8321e8d..7bb7250ba 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -27,7 +27,8 @@ from nova import wsgi
from nova import utils
from nova.api.openstack import common
from nova.api.openstack import faults
-from nova.api.openstack.views.servers import get_view_builder
+from nova.api.openstack.views import servers as servers_views
+from nova.api.openstack.views import addresses as addresses_views
from nova.auth import manager as auth_manager
from nova.compute import instance_types
from nova.compute import power_state
@@ -55,8 +56,8 @@ class Controller(wsgi.Controller):
def ips(self, req, id):
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- builder = get_view_builder(req)
- return builder._build_addresses(instance)
+ builder = addresses_views.get_view_builder(req)
+ return builder.build(instance)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -75,7 +76,7 @@ class Controller(wsgi.Controller):
"""
instance_list = self.compute_api.get_all(req.environ['nova.context'])
limited_list = common.limited(instance_list, req)
- builder = get_view_builder(req)
+ builder = servers_views.get_view_builder(req)
servers = [builder.build(inst, is_detail)['server']
for inst in limited_list]
return dict(servers=servers)
@@ -84,7 +85,7 @@ class Controller(wsgi.Controller):
""" Returns server details by server id """
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- builder = get_view_builder(req)
+ builder = servers_views.get_view_builder(req)
return builder.build(instance, is_detail=True)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
@@ -137,7 +138,7 @@ class Controller(wsgi.Controller):
metadata=metadata,
onset_files=env.get('onset_files', []))
- builder = get_view_builder(req)
+ builder = servers_views.get_view_builder(req)
server = builder.build(instances[0], is_detail=False)
password = "%s%s" % (server['server']['name'][:4],
utils.generate_password(12))
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 3ccfd8dba..950662747 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -1,6 +1,8 @@
import hashlib
from nova.compute import power_state
from nova.api.openstack.views import addresses as addresses_view
+from nova.api.openstack.views import flavors as flavors_view
+from nova.api.openstack.views import images as images_view
from nova import utils
@@ -12,7 +14,9 @@ def get_view_builder(req):
version = req.environ['nova.context'].version
addresses_builder = addresses_view.get_view_builder(req)
if version == '1.1':
- return ViewBuilder_1_1(addresses_builder)
+ flavor_builder = flavors_view.get_view_builder(req)
+ image_builder = images_view.get_view_builder(req)
+ return ViewBuilder_1_1(addresses_builder, flavor_builder, image_builder)
else:
return ViewBuilder_1_0(addresses_builder)
@@ -48,8 +52,10 @@ class ViewBuilder(object):
power_state.FAILED: 'error'}
inst_dict = {}
- mapped_keys = dict(status='state', imageId='image_id',
- flavorId='instance_type', name='display_name', id='id')
+ #mapped_keys = dict(status='state', imageId='image_id',
+ # flavorId='instance_type', name='display_name', id='id')
+
+ mapped_keys = dict(status='state', name='display_name', id='id')
for k, v in mapped_keys.iteritems():
inst_dict[k] = inst[v]
@@ -67,13 +73,39 @@ class ViewBuilder(object):
if inst['host']:
inst_dict['hostId'] = hashlib.sha224(inst['host']).hexdigest()
+ inst_dict = self._decorate_response(inst_dict, inst)
+
return dict(server=inst_dict)
+ def _build_image_data(self, response, inst):
+ raise NotImplementedError()
+
class ViewBuilder_1_0(ViewBuilder):
- pass
+ def _decorate_response(self, response, inst):
+ response["imageId"] = inst["image_id"]
+ response["flavorId"] = inst["instance_type"]
+ return response
class ViewBuilder_1_1(ViewBuilder):
- pass
+ def __init__(self, addresses_builder, flavor_builder, image_builder):
+ ViewBuilder.__init__(self, addresses_builder)
+ self.flavor_builder = flavor_builder
+ self.image_builder = image_builder
+
+ def _decorate_response(self, response, inst):
+ response = self._build_image_ref(response, inst)
+ response = self._build_flavor_ref(response, inst)
+ return response
+
+ def _build_image_ref(self, response, inst):
+ image_id = inst["image_id"]
+ response["imageRef"] = self.image_builder.generate_href(image_id)
+ return response
+
+ def _build_flavor_ref(self, response, inst):
+ flavor_id = inst["instance_type"]
+ response["flavorRef"]= self.flavor_builder.generate_href(flavor_id)
+ return response
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index b2446f194..b42cecfbb 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -79,7 +79,7 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None):
"admin_pass": "",
"user_id": user_id,
"project_id": "",
- "image_id": 10,
+ "image_id": "10",
"kernel_id": "",
"ramdisk_id": "",
"launch_index": 0,
@@ -92,7 +92,7 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None):
"local_gb": 0,
"hostname": "",
"host": None,
- "instance_type": "",
+ "instance_type": "1",
"user_data": "",
"reservation_id": "",
"mac_address": "",
@@ -353,7 +353,7 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
- def test_get_all_server_details(self):
+ def test_get_all_server_details_v1_0(self):
req = webob.Request.blank('/v1.0/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -363,7 +363,31 @@ class ServersTest(test.TestCase):
self.assertEqual(s['id'], i)
self.assertEqual(s['hostId'], '')
self.assertEqual(s['name'], 'server%d' % i)
- self.assertEqual(s['imageId'], 10)
+ self.assertEqual(s['imageId'], '10')
+ self.assertEqual(s['flavorId'], '1')
+ self.assertEqual(s['metadata']['seq'], i)
+ i += 1
+
+ def test_get_all_server_details_v1_1(self):
+ class FakeRequestContext(object):
+ def __init__(self, user, project, *args, **kwargs):
+ self.user_id = 1
+ self.project_id = 1
+ self.version = '1.1'
+ self.is_admin = True
+
+ self.stubs.Set(context, 'RequestContext', FakeRequestContext)
+ req = webob.Request.blank('/v1.1/servers/detail')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ i = 0
+ for s in res_dict['servers']:
+ self.assertEqual(s['id'], i)
+ self.assertEqual(s['hostId'], '')
+ self.assertEqual(s['name'], 'server%d' % i)
+ self.assertEqual(s['imageRef'], 'http://localhost/v1.1/images/10')
+ self.assertEqual(s['flavorRef'], 'http://localhost/v1.1/flavors/1')
self.assertEqual(s['metadata']['seq'], i)
i += 1
--
cgit
From 354f5e61c4bfb32ad8c2bc3389678f19db5fdb56 Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Tue, 15 Mar 2011 00:55:52 -0400
Subject: pep8 fixes
---
nova/api/openstack/servers.py | 3 ---
nova/api/openstack/views/servers.py | 12 +++++++-----
2 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 7bb7250ba..de67cbc4a 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -436,6 +436,3 @@ class Controller(wsgi.Controller):
_("Ramdisk not found for image %(image_id)s") % locals())
return kernel_id, ramdisk_id
-
-
-
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 950662747..15ac9964c 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -16,7 +16,8 @@ def get_view_builder(req):
if version == '1.1':
flavor_builder = flavors_view.get_view_builder(req)
image_builder = images_view.get_view_builder(req)
- return ViewBuilder_1_1(addresses_builder, flavor_builder, image_builder)
+ return ViewBuilder_1_1(addresses_builder, flavor_builder,
+ image_builder)
else:
return ViewBuilder_1_0(addresses_builder)
@@ -28,8 +29,10 @@ class ViewBuilder(object):
self.addresses_builder = addresses_builder
def build(self, inst, is_detail):
- """ Coerces into dictionary format, mapping everything to Rackspace-like
- attributes for return"""
+ """
+ Coerces into dictionary format, mapping everything to
+ Rackspace-like attributes for return
+ """
if is_detail:
return self._build_detail(inst)
else:
@@ -106,6 +109,5 @@ class ViewBuilder_1_1(ViewBuilder):
def _build_flavor_ref(self, response, inst):
flavor_id = inst["instance_type"]
- response["flavorRef"]= self.flavor_builder.generate_href(flavor_id)
+ response["flavorRef"] = self.flavor_builder.generate_href(flavor_id)
return response
-
--
cgit
From 2b20306fcaddcb6b9bc57fb55b17230d709cd1ce Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 22:23:38 -0700
Subject: Derive unit test from standard nova.test.TestCase
---
nova/tests/integrated/test_login.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py
index e362f92d6..5fa558bdf 100644
--- a/nova/tests/integrated/test_login.py
+++ b/nova/tests/integrated/test_login.py
@@ -18,6 +18,7 @@
import unittest
from nova import flags
+from nova import test
from nova.log import logging
from nova.tests.integrated import integrated_helpers
from nova.tests.integrated.api import client
@@ -29,7 +30,7 @@ FLAGS = flags.FLAGS
FLAGS.verbose = True
-class LoginTest(unittest.TestCase):
+class LoginTest(test.TestCase):
def setUp(self):
super(LoginTest, self).setUp()
context = integrated_helpers.IntegratedUnitTestContext.startup()
@@ -73,5 +74,6 @@ class LoginTest(unittest.TestCase):
self.assertRaises(client.OpenstackApiAuthenticationException,
bad_credentials_api.get_flavors)
+
if __name__ == "__main__":
unittest.main()
--
cgit
From db8beffc9acd90c748512c1fa9c127d39756232c Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 22:36:30 -0700
Subject: Reapplied rename of Openstack -> OpenStack. Easier to do it by hand
than to ask Bazaar to do it.
---
nova/tests/integrated/api/client.py | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index 6fba2930a..568e8c17e 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -24,7 +24,7 @@ from nova import log as logging
LOG = logging.getLogger('nova.tests.api')
-class OpenstackApiException(Exception):
+class OpenStackApiException(Exception):
def __init__(self, message=None, response=None):
self.response = response
if not message:
@@ -37,22 +37,22 @@ class OpenstackApiException(Exception):
message = _('%(message)s\nStatus Code: %(_status)s\n'
'Body: %(_body)s') % locals()
- super(OpenstackApiException, self).__init__(message)
+ super(OpenStackApiException, self).__init__(message)
-class OpenstackApiAuthenticationException(OpenstackApiException):
+class OpenStackApiAuthenticationException(OpenStackApiException):
def __init__(self, response=None, message=None):
if not message:
message = _("Authentication error")
- super(OpenstackApiAuthenticationException, self).__init__(message,
+ super(OpenStackApiAuthenticationException, self).__init__(message,
response)
-class OpenstackApiNotFoundException(OpenstackApiException):
+class OpenStackApiNotFoundException(OpenStackApiException):
def __init__(self, response=None, message=None):
if not message:
message = _("Item not found")
- super(OpenstackApiNotFoundException, self).__init__(message, response)
+ super(OpenStackApiNotFoundException, self).__init__(message, response)
class TestOpenStackClient(object):
@@ -82,7 +82,7 @@ class TestOpenStackClient(object):
conn = httplib.HTTPSConnection(hostname,
port=port)
else:
- raise OpenstackApiException("Unknown scheme: %s" % url)
+ raise OpenStackApiException("Unknown scheme: %s" % url)
relative_url = parsed_url.path
if parsed_url.query:
@@ -112,7 +112,7 @@ class TestOpenStackClient(object):
# bug732866
#if http_status == 401:
if http_status != 204:
- raise OpenstackApiAuthenticationException(response=response)
+ raise OpenStackApiAuthenticationException(response=response)
auth_headers = {}
for k, v in response.getheaders():
@@ -139,9 +139,9 @@ class TestOpenStackClient(object):
if check_response_status:
if not http_status in check_response_status:
if http_status == 404:
- raise OpenstackApiNotFoundException(response=response)
+ raise OpenStackApiNotFoundException(response=response)
else:
- raise OpenstackApiException(
+ raise OpenStackApiException(
message=_("Unexpected status code"),
response=response)
--
cgit
From e0563f49792441af106c52e662bdada3c7997feb Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Mon, 14 Mar 2011 22:43:21 -0700
Subject: Reapplied rename to another file.
---
nova/tests/integrated/test_login.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/tests/integrated/test_login.py b/nova/tests/integrated/test_login.py
index 5fa558bdf..501f8c919 100644
--- a/nova/tests/integrated/test_login.py
+++ b/nova/tests/integrated/test_login.py
@@ -53,7 +53,7 @@ class LoginTest(test.TestCase):
"notso_password",
self.user.auth_url)
- self.assertRaises(client.OpenstackApiAuthenticationException,
+ self.assertRaises(client.OpenStackApiAuthenticationException,
bad_credentials_api.get_flavors)
def test_bad_login_username(self):
@@ -62,7 +62,7 @@ class LoginTest(test.TestCase):
self.user.secret,
self.user.auth_url)
- self.assertRaises(client.OpenstackApiAuthenticationException,
+ self.assertRaises(client.OpenStackApiAuthenticationException,
bad_credentials_api.get_flavors)
def test_bad_login_both_bad(self):
@@ -71,7 +71,7 @@ class LoginTest(test.TestCase):
"notso_password",
self.user.auth_url)
- self.assertRaises(client.OpenstackApiAuthenticationException,
+ self.assertRaises(client.OpenStackApiAuthenticationException,
bad_credentials_api.get_flavors)
--
cgit
From 8a41046dc7cafb19afb6719866b11681daaa9082 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Tue, 15 Mar 2011 09:48:21 +0100
Subject: Always put the ipv6 fallback in place. FLAGS.use_ipv6 does not exist
yet when the firewall driver is instantiated and the iptables manager takes
care not to fiddle with ipv6 if not enabled.
---
nova/virt/libvirt_conn.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 03f046cbd..f87decaa0 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1597,9 +1597,8 @@ class IptablesFirewallDriver(FirewallDriver):
self.iptables.ipv4['filter'].add_chain('sg-fallback')
self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP')
- if FLAGS.use_ipv6:
- self.iptables.ipv6['filter'].add_chain('sg-fallback')
- self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP')
+ self.iptables.ipv6['filter'].add_chain('sg-fallback')
+ self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP')
def setup_basic_filtering(self, instance):
"""Use NWFilter from libvirt for this."""
--
cgit
From e161b00349a7478ac9f51f087c9f16cd345bc2d2 Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Tue, 15 Mar 2011 13:23:42 -0400
Subject: adding missing view modules; modifying a couple of servers tests to
use enumerate
---
nova/api/openstack/views/addresses.py | 38 +++++++++++++++++++++++++++++++
nova/api/openstack/views/flavors.py | 39 ++++++++++++++++++++++++++++++++
nova/api/openstack/views/images.py | 39 ++++++++++++++++++++++++++++++++
nova/tests/api/openstack/test_servers.py | 8 ++-----
4 files changed, 118 insertions(+), 6 deletions(-)
create mode 100644 nova/api/openstack/views/addresses.py
create mode 100644 nova/api/openstack/views/flavors.py
create mode 100644 nova/api/openstack/views/images.py
diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py
new file mode 100644
index 000000000..d764e5229
--- /dev/null
+++ b/nova/api/openstack/views/addresses.py
@@ -0,0 +1,38 @@
+import hashlib
+from nova.compute import power_state
+from nova import utils
+
+
+def get_view_builder(req):
+ '''
+ A factory method that returns the correct builder based on the version of
+ the api requested.
+ '''
+ version = req.environ['nova.context'].version
+ if version == '1.1':
+ return ViewBuilder_1_1()
+ else:
+ return ViewBuilder_1_0()
+
+
+class ViewBuilder(object):
+ ''' Models a server addresses response as a python dictionary.'''
+
+ def build(self, inst):
+ raise NotImplementedError()
+
+
+class ViewBuilder_1_0(ViewBuilder):
+ def build(self, inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ return dict(public=public_ips, private=private_ips)
+
+
+class ViewBuilder_1_1(ViewBuilder):
+ def build(self, inst):
+ private_ips = utils.get_from_path(inst, 'fixed_ip/address')
+ private_ips = [dict(version=4, addr=a) for a in private_ips]
+ public_ips = utils.get_from_path(inst, 'fixed_ip/floating_ips/address')
+ public_ips = [dict(version=4, addr=a) for a in public_ips]
+ return dict(public=public_ips, private=private_ips)
diff --git a/nova/api/openstack/views/flavors.py b/nova/api/openstack/views/flavors.py
new file mode 100644
index 000000000..c6b6c10bb
--- /dev/null
+++ b/nova/api/openstack/views/flavors.py
@@ -0,0 +1,39 @@
+
+
+def get_view_builder(req):
+ '''
+ A factory method that returns the correct builder based on the version of
+ the api requested.
+ '''
+ version = req.environ['nova.context'].version
+ base_url = req.application_url
+ if version == '1.1':
+ return ViewBuilder_1_1(base_url)
+ else:
+ return ViewBuilder_1_0()
+
+
+class ViewBuilder(object):
+ def __init__(self):
+ pass
+
+ def build(self, flavor_obj):
+ raise NotImplementedError()
+
+ def _decorate_response(self, response, flavor_obj):
+ return response
+
+
+class ViewBuilder_1_1(ViewBuilder):
+ def __init__(self, base_url):
+ self.base_url = base_url
+
+ def _decorate_response(self, response, flavor_obj):
+ raise NotImplementedError()
+
+ def generate_href(self, flavor_id):
+ return "{0}/flavors/{1}".format(self.base_url, flavor_id)
+
+
+class ViewBuilder_1_0(ViewBuilder):
+ pass
diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py
new file mode 100644
index 000000000..c80713250
--- /dev/null
+++ b/nova/api/openstack/views/images.py
@@ -0,0 +1,39 @@
+
+
+def get_view_builder(req):
+ '''
+ A factory method that returns the correct builder based on the version of
+ the api requested.
+ '''
+ version = req.environ['nova.context'].version
+ base_url = req.application_url
+ if version == '1.1':
+ return ViewBuilder_1_1(base_url)
+ else:
+ return ViewBuilder_1_0()
+
+
+class ViewBuilder(object):
+ def __init__(self):
+ pass
+
+ def build(self, image_obj):
+ raise NotImplementedError()
+
+ def _decorate_response(self, response, image_obj):
+ return response
+
+
+class ViewBuilder_1_1(ViewBuilder):
+ def __init__(self, base_url):
+ self.base_url = base_url
+
+ def _decorate_response(self, response, image_obj):
+ raise NotImplementedError()
+
+ def generate_href(self, image_id):
+ return "{0}/images/{1}".format(self.base_url, image_id)
+
+
+class ViewBuilder_1_0(ViewBuilder):
+ pass
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index b42cecfbb..ad2fa2497 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -358,15 +358,13 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
- i = 0
- for s in res_dict['servers']:
+ for i,s in enumerate(res_dict['servers']):
self.assertEqual(s['id'], i)
self.assertEqual(s['hostId'], '')
self.assertEqual(s['name'], 'server%d' % i)
self.assertEqual(s['imageId'], '10')
self.assertEqual(s['flavorId'], '1')
self.assertEqual(s['metadata']['seq'], i)
- i += 1
def test_get_all_server_details_v1_1(self):
class FakeRequestContext(object):
@@ -381,15 +379,13 @@ class ServersTest(test.TestCase):
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
- i = 0
- for s in res_dict['servers']:
+ for i,s in enumerate(res_dict['servers']):
self.assertEqual(s['id'], i)
self.assertEqual(s['hostId'], '')
self.assertEqual(s['name'], 'server%d' % i)
self.assertEqual(s['imageRef'], 'http://localhost/v1.1/images/10')
self.assertEqual(s['flavorRef'], 'http://localhost/v1.1/flavors/1')
self.assertEqual(s['metadata']['seq'], i)
- i += 1
def test_get_all_server_details_with_host(self):
'''
--
cgit
From 56ff68056254610c4f0eb5cd5c5432a68ed30b2f Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Tue, 15 Mar 2011 10:42:32 -0700
Subject: Support testing the OpenStack API without key_pairs
---
nova/tests/api/openstack/fakes.py | 11 +++++++++--
nova/tests/api/openstack/test_servers.py | 9 ++++++++-
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index e50d11a3d..ccc853360 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -85,10 +85,17 @@ def wsgi_app(inner_application=None):
return mapper
-def stub_out_key_pair_funcs(stubs):
+def stub_out_key_pair_funcs(stubs, have_key_pair=True):
def key_pair(context, user_id):
return [dict(name='key', public_key='public_key')]
- stubs.Set(nova.db, 'key_pair_get_all_by_user', key_pair)
+
+ def no_key_pair(context, user_id):
+ return []
+
+ if have_key_pair:
+ stubs.Set(nova.db, 'key_pair_get_all_by_user', key_pair)
+ else:
+ stubs.Set(nova.db, 'key_pair_get_all_by_user', no_key_pair)
def stub_out_image_service(stubs):
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 5d7a208e9..40026a615 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -216,7 +216,7 @@ class ServersTest(test.TestCase):
servers = json.loads(res.body)['servers']
self.assertEqual([s['id'] for s in servers], [1, 2])
- def test_create_instance(self):
+ def _test_create_instance_helper(self, with_key_pair):
def instance_create(context, inst):
return {'id': '1', 'display_name': 'server_test'}
@@ -271,6 +271,13 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
+ def test_create_instance(self):
+ self._test_create_instance_helper(True)
+
+ def test_create_instance_no_key_pair(self):
+ fakes.stub_out_key_pair_funcs(self.stubs, False)
+ self._test_create_instance_helper(False)
+
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req.method = 'PUT'
--
cgit
From 22aad6700124411aceed0b2bd3953cbbc48b6130 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Tue, 15 Mar 2011 11:24:07 -0700
Subject: Use random.SystemRandom for easy secure randoms, configurable symbol
set by default including mixed-case
---
nova/utils.py | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/nova/utils.py b/nova/utils.py
index 0510c3cbe..199ee8701 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -262,19 +262,25 @@ def generate_mac():
return ':'.join(map(lambda x: "%02x" % x, mac))
-def generate_password(length=20):
- """Generate a random alphanumeric password, avoiding 'confusing' O,0,I,1.
+# Default symbols to use for passwords. Avoids visually confusing characters.
+# ~6 bits per symbol
+DEFAULT_PASSWORD_SYMBOLS = ("23456789" # Removed: 0,1
+ "ABCDEFGHJKLMNPQRSTUVWXYZ" # Removed: I, O
+ "abcdefghijkmnopqrstuvwxyz") # Removed: l
+
+
+# ~5 bits per symbol
+EASIER_PASSWORD_SYMBOLS = ("23456789" # Removed: 0, 1
+ "ABCDEFGHJKLMNPQRSTUVWXYZ") # Removed: I, O
+
+
+def generate_password(length=20, symbols=DEFAULT_PASSWORD_SYMBOLS):
+ """Generate a random password from the supplied symbols.
Believed to be reasonably secure (with a reasonable password length!)
"""
- # 26 letters, 10 digits = 36 choices
- # Remove O, 0, I, 1 => 32 choices
- # 32 choices means we're just using the low 5 bit of each byte,
- # so there's no bias introduced by using a modulo
- chrs = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"
-
- random_bytes = os.urandom(length)
- return "".join([chrs[ord(random_bytes[i]) % 32] for i in xrange(length)])
+ r = random.SystemRandom()
+ return "".join([r.choice(symbols) for _i in xrange(length)])
def last_octet(address):
--
cgit
From 937c135ec0c8b557b22ad30c400c75c713f660e1 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Tue, 15 Mar 2011 14:31:48 -0400
Subject: Code clean up. Removing _decorate_response methods. Replaced them
with more explicit methods, _build_image, and _build_flavor.
---
nova/api/openstack/views/flavors.py | 6 ------
nova/api/openstack/views/images.py | 6 ------
nova/api/openstack/views/servers.py | 30 ++++++++++++++++--------------
3 files changed, 16 insertions(+), 26 deletions(-)
diff --git a/nova/api/openstack/views/flavors.py b/nova/api/openstack/views/flavors.py
index c6b6c10bb..dfcc2644c 100644
--- a/nova/api/openstack/views/flavors.py
+++ b/nova/api/openstack/views/flavors.py
@@ -20,17 +20,11 @@ class ViewBuilder(object):
def build(self, flavor_obj):
raise NotImplementedError()
- def _decorate_response(self, response, flavor_obj):
- return response
-
class ViewBuilder_1_1(ViewBuilder):
def __init__(self, base_url):
self.base_url = base_url
- def _decorate_response(self, response, flavor_obj):
- raise NotImplementedError()
-
def generate_href(self, flavor_id):
return "{0}/flavors/{1}".format(self.base_url, flavor_id)
diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py
index c80713250..cd61ed656 100644
--- a/nova/api/openstack/views/images.py
+++ b/nova/api/openstack/views/images.py
@@ -20,17 +20,11 @@ class ViewBuilder(object):
def build(self, image_obj):
raise NotImplementedError()
- def _decorate_response(self, response, image_obj):
- return response
-
class ViewBuilder_1_1(ViewBuilder):
def __init__(self, base_url):
self.base_url = base_url
- def _decorate_response(self, response, image_obj):
- raise NotImplementedError()
-
def generate_href(self, image_id):
return "{0}/images/{1}".format(self.base_url, image_id)
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 15ac9964c..708c74b4e 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -23,7 +23,10 @@ def get_view_builder(req):
class ViewBuilder(object):
- ''' Models a server response as a python dictionary.'''
+ '''
+ Models a server response as a python dictionary.
+ Abstract methods: _build_image, _build_flavor
+ '''
def __init__(self, addresses_builder):
self.addresses_builder = addresses_builder
@@ -76,19 +79,24 @@ class ViewBuilder(object):
if inst['host']:
inst_dict['hostId'] = hashlib.sha224(inst['host']).hexdigest()
- inst_dict = self._decorate_response(inst_dict, inst)
+ self._build_image(inst_dict, inst)
+ self._build_flavor(inst_dict, inst)
return dict(server=inst_dict)
- def _build_image_data(self, response, inst):
+ def _build_image(self, response, inst):
+ raise NotImplementedError()
+
+ def _build_flavor(self, response, inst):
raise NotImplementedError()
class ViewBuilder_1_0(ViewBuilder):
- def _decorate_response(self, response, inst):
+ def _build_image(self, response, inst):
response["imageId"] = inst["image_id"]
+
+ def _build_flavor(self, response, inst):
response["flavorId"] = inst["instance_type"]
- return response
class ViewBuilder_1_1(ViewBuilder):
@@ -97,17 +105,11 @@ class ViewBuilder_1_1(ViewBuilder):
self.flavor_builder = flavor_builder
self.image_builder = image_builder
- def _decorate_response(self, response, inst):
- response = self._build_image_ref(response, inst)
- response = self._build_flavor_ref(response, inst)
- return response
-
- def _build_image_ref(self, response, inst):
+ def _build_image(self, response, inst):
image_id = inst["image_id"]
response["imageRef"] = self.image_builder.generate_href(image_id)
- return response
- def _build_flavor_ref(self, response, inst):
+ def _build_flavor(self, response, inst):
flavor_id = inst["instance_type"]
response["flavorRef"] = self.flavor_builder.generate_href(flavor_id)
- return response
+
--
cgit
From 1d69d499124317aa1a9cf7d4bc54db2ff0bc3be9 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 15 Mar 2011 14:33:45 -0400
Subject: refactor onset_files quota checking
---
nova/compute/api.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index b6ef889f6..c11059a28 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -86,6 +86,8 @@ class API(base.Base):
Raises a QuotaError if any limit is exceeded
"""
+ if onset_files is None:
+ return
limit = quota.allowed_onset_files(context)
if len(onset_files) > limit:
raise quota.QuotaError(code="OnsetFileLimitExceeded")
@@ -96,7 +98,6 @@ class API(base.Base):
raise quota.QuotaError(code="OnsetFilePathLimitExceeded")
if len(content) > content_limit:
raise quota.QuotaError(code="OnsetFileContentLimitExceeded")
- return onset_files
def create(self, context, instance_type,
image_id, kernel_id=None, ramdisk_id=None,
@@ -142,8 +143,7 @@ class API(base.Base):
LOG.warn(msg)
raise quota.QuotaError(msg, "MetadataLimitExceeded")
- if onset_files is not None:
- onset_files = self._check_onset_file_quota(context, onset_files)
+ self._check_onset_file_quota(context, onset_files)
image = self.image_service.show(context, image_id)
--
cgit
From cc25d277755f0e103ff09144d1d490536ab9acec Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Tue, 15 Mar 2011 15:56:54 -0400
Subject: modifying paste config to support v1.1; adding v1.1 entry in versions
resource ( GET /)
---
etc/api-paste.ini | 1 +
nova/api/openstack/__init__.py | 7 +++++--
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/etc/api-paste.ini b/etc/api-paste.ini
index 9f7e93d4c..a4483d3f8 100644
--- a/etc/api-paste.ini
+++ b/etc/api-paste.ini
@@ -68,6 +68,7 @@ paste.app_factory = nova.api.ec2.metadatarequesthandler:MetadataRequestHandler.f
use = egg:Paste#urlmap
/: osversions
/v1.0: openstackapi
+/v1.1: openstackapi
[pipeline:openstackapi]
pipeline = faultwrap auth ratelimit osapiapp
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index ce3cff337..0244bc93c 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -128,8 +128,11 @@ class Versions(wsgi.Application):
def __call__(self, req):
"""Respond to a request for all OpenStack API versions."""
response = {
- "versions": [
- dict(status="CURRENT", id="v1.0")]}
+ "versions": [
+ dict(status="DEPRECATED", id="v1.0"),
+ dict(status="CURRENT", id="v1.1"),
+ ],
+ }
metadata = {
"application/xml": {
"attributes": dict(version=["status", "id"])}}
--
cgit
From 4b49df7e7232dfd3e187faac52b9eb72773be360 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Tue, 15 Mar 2011 16:49:19 -0400
Subject: Major cosmetic changes to limits, but little-to-no functional
changes. MUCH better testability now, no more relying on system time to tick
by for limit testing.
---
etc/api-paste.ini | 2 +-
nova/api/openstack/__init__.py | 6 +
nova/api/openstack/faults.py | 21 ++
nova/api/openstack/limits.py | 342 ++++++++++++++++++++
nova/tests/api/openstack/__init__.py | 2 +-
nova/tests/api/openstack/fakes.py | 10 +-
nova/tests/api/openstack/test_adminapi.py | 1 -
nova/tests/api/openstack/test_ratelimiting.py | 443 +++++++++++++++++---------
8 files changed, 669 insertions(+), 158 deletions(-)
create mode 100644 nova/api/openstack/limits.py
diff --git a/etc/api-paste.ini b/etc/api-paste.ini
index 9f7e93d4c..750f0ad87 100644
--- a/etc/api-paste.ini
+++ b/etc/api-paste.ini
@@ -79,7 +79,7 @@ paste.filter_factory = nova.api.openstack:FaultWrapper.factory
paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory
[filter:ratelimit]
-paste.filter_factory = nova.api.openstack.ratelimiting:RateLimitingMiddleware.factory
+paste.filter_factory = nova.api.openstack.limits:RateLimitingMiddleware.factory
[app:osapiapp]
paste.app_factory = nova.api.openstack:APIRouter.factory
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index ce3cff337..db2dff8d5 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -33,6 +33,7 @@ from nova.api.openstack import backup_schedules
from nova.api.openstack import consoles
from nova.api.openstack import flavors
from nova.api.openstack import images
+from nova.api.openstack import limits
from nova.api.openstack import servers
from nova.api.openstack import shared_ip_groups
from nova.api.openstack import users
@@ -114,12 +115,17 @@ class APIRouter(wsgi.Router):
mapper.resource("image", "images", controller=images.Controller(),
collection={'detail': 'GET'})
+
mapper.resource("flavor", "flavors", controller=flavors.Controller(),
collection={'detail': 'GET'})
+
mapper.resource("shared_ip_group", "shared_ip_groups",
collection={'detail': 'GET'},
controller=shared_ip_groups.Controller())
+ _limits = limits.LimitsController()
+ mapper.resource("limit", "limits", controller=_limits)
+
super(APIRouter, self).__init__(mapper)
diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py
index 2fd733299..6ed9322de 100644
--- a/nova/api/openstack/faults.py
+++ b/nova/api/openstack/faults.py
@@ -61,3 +61,24 @@ class Fault(webob.exc.HTTPException):
content_type = req.best_match_content_type()
self.wrapped_exc.body = serializer.serialize(fault_data, content_type)
return self.wrapped_exc
+
+
+class OverLimitFault(webob.exc.HTTPException):
+ """
+ Rate-limited request response.
+ """
+
+ wrapped_exc = webob.exc.HTTPForbidden()
+
+ def __init__(self, message, details, retry_time):
+ """
+ Initialize new `OverLimitFault` with relevant information.
+ """
+ self.message = message
+ self.details = details
+ self.retry_time = retry_time
+
+ @webob.dec.wsgify(RequestClass=wsgi.Request)
+ def __call__(self, request):
+ """Currently just return the wrapped exception."""
+ return self.wrapped_exc
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
new file mode 100644
index 000000000..4f64cab3c
--- /dev/null
+++ b/nova/api/openstack/limits.py
@@ -0,0 +1,342 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack LLC.
+# 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 datetime
+
+"""
+Module dedicated functions/classes dealing with rate limiting requests.
+"""
+
+import copy
+import httplib
+import json
+import math
+import re
+import time
+import urllib
+import webob.exc
+
+from collections import defaultdict
+
+from webob.dec import wsgify
+
+from nova import wsgi
+from nova.api.openstack import faults
+from nova.wsgi import Controller
+from nova.wsgi import Middleware
+
+
+# Convenience constants for the limits dictionary passed to Limiter().
+PER_SECOND = 1
+PER_MINUTE = 60
+PER_HOUR = 60 * 60
+PER_DAY = 60 * 60 * 24
+
+
+class LimitsController(Controller):
+ """
+ Controller for accessing limits in the OpenStack API.
+ """
+
+ def index(self, req):
+ """
+ Return all global and rate limit information.
+ """
+ abs_limits = {}
+ rate_limits = req.environ.get("nova.limits", {})
+
+ return {
+ "limits" : {
+ "rate" : rate_limits,
+ "absolute" : abs_limits,
+ },
+ }
+
+
+class Limit(object):
+ """
+ Stores information about a limit for HTTP requets.
+ """
+
+ UNITS = {
+ 1 : "SECOND",
+ 60 : "MINUTE",
+ 60 * 60 : "HOUR",
+ 60 * 60 * 24 : "DAY",
+ }
+
+ def __init__(self, verb, uri, regex, value, unit):
+ """
+ Initialize a new `Limit`.
+
+ @param verb: HTTP verb (POST, PUT, etc.)
+ @param uri: Human-readable URI
+ @param regex: Regular expression format for this limit
+ @param value: Integer number of requests which can be made
+ @param unit: Unit of measure for the value parameter
+ """
+ self.verb = verb
+ self.uri = uri
+ self.regex = regex
+ self.value = int(value)
+ self.unit = unit
+ self.remaining = int(value)
+
+ if value <= 0:
+ raise ValueError("Limit value must be > 0")
+
+ self.last_request = None
+ self.next_request = None
+
+ self.water_level = 0
+ self.capacity = float(self.unit)
+ self.request_value = float(self.capacity) / float(self.value)
+
+ def __call__(self, verb, url):
+ """
+ Represents a call to this limit from a relevant request.
+
+ @param verb: string http verb (POST, GET, etc.)
+ @param url: string URL
+ """
+ if self.verb != verb or not re.match(self.regex, url):
+ return
+
+ now = self._get_time()
+
+ if self.last_request is None:
+ self.last_request = now
+
+ leak_value = now - self.last_request
+
+ self.water_level -= leak_value
+ self.water_level = max(self.water_level, 0)
+ self.water_level += self.request_value
+
+ difference = self.water_level - self.capacity
+
+ self.last_request = now
+
+ if difference > 0:
+ self.water_level -= self.request_value
+ self.next_request = now + difference
+ return difference
+
+ cap = self.capacity
+ water = self.water_level
+ val = self.value
+
+ self.remaining = math.floor((cap - water) / cap * val)
+ print "Remaining:", self.remaining
+ self.next_request = now
+
+ def _get_time(self):
+ """Retrieve the current time. Broken out for testability."""
+ return time.time()
+
+ def display_unit(self):
+ """Display the string name of the unit."""
+ return self.UNITS.get(self.unit, "UNKNOWN")
+
+ def display(self):
+ """Return a useful representation of this class."""
+ return {
+ "verb" : self.verb,
+ "uri" : self.uri,
+ "regex" : self.regex,
+ "value" : self.value,
+ "remaining" : int(self.remaining),
+ "unit" : self.display_unit(),
+ "resetTime" : int(self.next_request or self._get_time()),
+ }
+
+
+
+# "Limit" format is a dictionary with the HTTP verb, human-readable URI,
+# a regular-expression to match, value and unit of measure (PER_DAY, etc.)
+
+DEFAULT_LIMITS = [
+ Limit("POST", "*", ".*", 10, PER_MINUTE),
+ Limit("POST", "*/servers", "^/servers", 50, PER_DAY),
+ Limit("PUT", "*", ".*", 10, PER_MINUTE),
+ Limit("GET", "*changes-since*", ".*changes-since.*", 3, PER_MINUTE),
+ Limit("DELETE", "*", ".*", 100, PER_MINUTE),
+]
+
+
+class RateLimitingMiddleware(Middleware):
+ """
+ Rate-limits requests passing through this middleware. All limit information
+ is stored in memory for this implementation.
+ """
+
+ def __init__(self, application, limits=None):
+ """
+ Initialize new `RateLimitingMiddleware`, which wraps the given WSGI
+ application and sets up the given limits.
+
+ @param application: WSGI application to wrap
+ @param limits: List of dictionaries describing limits
+ """
+ Middleware.__init__(self, application)
+ self._limiter = Limiter(limits or DEFAULT_LIMITS)
+
+ @wsgify(RequestClass=wsgi.Request)
+ def __call__(self, req):
+ """
+ Represents a single call through this middleware. We should record the
+ request if we have a limit relevant to it. If no limit is relevant to
+ the request, ignore it.
+
+ If the request should be rate limited, return a fault telling the user
+ they are over the limit and need to retry later.
+ """
+ verb = req.method
+ url = req.url
+ username = req.environ["nova.context"].user_id
+
+ delay = self._limiter.check_for_delay(verb, url, username)
+
+ if delay:
+ msg = "This request was rate-limited."
+ details = "Error details."
+ retry = time.time() + delay
+ return faults.OverLimitFault(msg, details, retry)
+
+ req.environ["nova.limits"] = self._limiter.get_limits(username)
+
+ return self.application
+
+
+class Limiter(object):
+ """
+ Rate-limit checking class which handles limits in memory.
+ """
+
+ def __init__(self, limits):
+ """
+ Initialize the new `Limiter`.
+
+ @param limits: List of `Limit` objects
+ """
+ self.limits = copy.deepcopy(limits)
+ self.levels = defaultdict(lambda: copy.deepcopy(limits))
+
+ def get_limits(self, username=None):
+ """
+ Return the limits for a given user.
+ """
+ return [limit.display() for limit in self.levels[username]]
+
+ def check_for_delay(self, verb, url, username=None):
+ """
+ Check the given verb/user/user triplet for limit.
+ """
+ def _get_delay_list():
+ """Yield limit delays."""
+ for limit in self.levels[username]:
+ delay = limit(verb, url)
+ if delay:
+ yield delay
+
+ delays = list(_get_delay_list())
+
+ if delays:
+ delays.sort()
+ return delays[0]
+
+
+class WsgiLimiter(object):
+ """
+ Rate-limit checking from a WSGI application. Uses an in-memory `Limiter`.
+
+ To use:
+ POST / with JSON data such as:
+ {
+ "verb" : GET,
+ "path" : "/servers"
+ }
+
+ and receive a 204 No Content, or a 403 Forbidden with an X-Wait-Seconds
+ header containing the number of seconds to wait before the action would
+ succeed.
+ """
+
+ def __init__(self, limits=None):
+ """
+ Initialize the new `WsgiLimiter`.
+
+ @param limits: List of `Limit` objects
+ """
+ self._limiter = Limiter(limits or DEFAULT_LIMITS)
+
+ @wsgify(RequestClass=wsgi.Request)
+ def __call__(self, request):
+ """
+ Handles a call to this application. Returns 204 if the request is
+ acceptable to the limiter, else a 403 is returned with a relevant
+ header indicating when the request *will* succeed.
+ """
+ if request.method != "POST":
+ raise webob.exc.HTTPMethodNotAllowed()
+
+ try:
+ info = dict(json.loads(request.body))
+ except ValueError:
+ raise webob.exc.HTTPBadRequest()
+
+ username = request.path_info_pop()
+ verb = info.get("verb")
+ path = info.get("path")
+
+ delay = self._limiter.check_for_delay(verb, path, username)
+
+ if delay:
+ headers = {"X-Wait-Seconds": "%.2f" % delay}
+ return webob.exc.HTTPForbidden(headers=headers)
+ else:
+ return webob.exc.HTTPNoContent()
+
+
+class WsgiLimiterProxy(object):
+ """
+ Rate-limit requests based on answers from a remote source.
+ """
+
+ def __init__(self, limiter_address):
+ """
+ Initialize the new `WsgiLimiterProxy`.
+
+ @param limiter_address: IP/port combination of where to request limit
+ """
+ self.limiter_address = limiter_address
+
+ def check_for_delay(self, verb, path, username=None):
+ body = json.dumps({"verb":verb,"path":path})
+ headers = {"Content-Type" : "application/json"}
+
+ conn = httplib.HTTPConnection(self.limiter_address)
+
+ if username:
+ conn.request("POST", "/%s" % (username), body, headers)
+ else:
+ conn.request("POST", "/", body, headers)
+
+ resp = conn.getresponse()
+
+ if 200 >= resp.status < 300:
+ return None
+
+ return resp.getheader("X-Wait-Seconds")
diff --git a/nova/tests/api/openstack/__init__.py b/nova/tests/api/openstack/__init__.py
index e18120285..bac7181f7 100644
--- a/nova/tests/api/openstack/__init__.py
+++ b/nova/tests/api/openstack/__init__.py
@@ -20,7 +20,7 @@ from nova import test
from nova import context
from nova import flags
-from nova.api.openstack.ratelimiting import RateLimitingMiddleware
+from nova.api.openstack.limits import RateLimitingMiddleware
from nova.api.openstack.common import limited
from nova.tests.api.openstack import fakes
from webob import Request
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 7cb974bb2..ae95c0648 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -34,7 +34,7 @@ from nova import utils
import nova.api.openstack.auth
from nova.api import openstack
from nova.api.openstack import auth
-from nova.api.openstack import ratelimiting
+from nova.api.openstack import limits
from nova.auth.manager import User, Project
from nova.image import glance
from nova.image import local
@@ -79,7 +79,7 @@ def wsgi_app(inner_application=None):
inner_application = openstack.APIRouter()
mapper = urlmap.URLMap()
api = openstack.FaultWrapper(auth.AuthMiddleware(
- ratelimiting.RateLimitingMiddleware(inner_application)))
+ limits.RateLimitingMiddleware(inner_application)))
mapper['/v1.0'] = api
mapper['/'] = openstack.FaultWrapper(openstack.Versions())
return mapper
@@ -110,13 +110,13 @@ def stub_out_auth(stubs):
def stub_out_rate_limiting(stubs):
def fake_rate_init(self, app):
- super(ratelimiting.RateLimitingMiddleware, self).__init__(app)
+ super(limits.RateLimitingMiddleware, self).__init__(app)
self.application = app
- stubs.Set(nova.api.openstack.ratelimiting.RateLimitingMiddleware,
+ stubs.Set(nova.api.openstack.limits.RateLimitingMiddleware,
'__init__', fake_rate_init)
- stubs.Set(nova.api.openstack.ratelimiting.RateLimitingMiddleware,
+ stubs.Set(nova.api.openstack.limits.RateLimitingMiddleware,
'__call__', fake_wsgi)
diff --git a/nova/tests/api/openstack/test_adminapi.py b/nova/tests/api/openstack/test_adminapi.py
index 4568cb9f5..e87255b18 100644
--- a/nova/tests/api/openstack/test_adminapi.py
+++ b/nova/tests/api/openstack/test_adminapi.py
@@ -23,7 +23,6 @@ from paste import urlmap
from nova import flags
from nova import test
from nova.api import openstack
-from nova.api.openstack import ratelimiting
from nova.api.openstack import auth
from nova.tests.api.openstack import fakes
diff --git a/nova/tests/api/openstack/test_ratelimiting.py b/nova/tests/api/openstack/test_ratelimiting.py
index 9ae90ee20..c88de2db7 100644
--- a/nova/tests/api/openstack/test_ratelimiting.py
+++ b/nova/tests/api/openstack/test_ratelimiting.py
@@ -1,178 +1,321 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack LLC.
+# 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.
+
+"""
+Tests dealing with HTTP rate-limiting.
+"""
+
import httplib
+import json
import StringIO
+import stubout
import time
import webob
from nova import test
-import nova.api.openstack.ratelimiting as ratelimiting
+from nova.api.openstack import limits
+from nova.api.openstack.limits import Limit
+
+TEST_LIMITS = [
+ Limit("GET", "/delayed", "^/delayed", 1, limits.PER_MINUTE),
+ Limit("POST", "*", ".*", 7, limits.PER_MINUTE),
+ Limit("POST", "/servers", "^/servers", 3, limits.PER_MINUTE),
+ Limit("PUT", "*", "", 10, limits.PER_MINUTE),
+ Limit("PUT", "/servers", "^/servers", 5, limits.PER_MINUTE),
+]
class LimiterTest(test.TestCase):
+ """
+ Tests for the in-memory `limits.Limiter` class.
+ """
def setUp(self):
- super(LimiterTest, self).setUp()
- self.limits = {
- 'a': (5, ratelimiting.PER_SECOND),
- 'b': (5, ratelimiting.PER_MINUTE),
- 'c': (5, ratelimiting.PER_HOUR),
- 'd': (1, ratelimiting.PER_SECOND),
- 'e': (100, ratelimiting.PER_SECOND)}
- self.rl = ratelimiting.Limiter(self.limits)
-
- def exhaust(self, action, times_until_exhausted, **kwargs):
- for i in range(times_until_exhausted):
- when = self.rl.perform(action, **kwargs)
- self.assertEqual(when, None)
- num, period = self.limits[action]
- delay = period * 1.0 / num
- # Verify that we are now thoroughly delayed
- for i in range(10):
- when = self.rl.perform(action, **kwargs)
- self.assertAlmostEqual(when, delay, 2)
-
- def test_second(self):
- self.exhaust('a', 5)
- time.sleep(0.2)
- self.exhaust('a', 1)
- time.sleep(1)
- self.exhaust('a', 5)
-
- def test_minute(self):
- self.exhaust('b', 5)
-
- def test_one_per_period(self):
- def allow_once_and_deny_once():
- when = self.rl.perform('d')
- self.assertEqual(when, None)
- when = self.rl.perform('d')
- self.assertAlmostEqual(when, 1, 2)
- return when
- time.sleep(allow_once_and_deny_once())
- time.sleep(allow_once_and_deny_once())
- allow_once_and_deny_once()
-
- def test_we_can_go_indefinitely_if_we_spread_out_requests(self):
- for i in range(200):
- when = self.rl.perform('e')
- self.assertEqual(when, None)
- time.sleep(0.01)
-
- def test_users_get_separate_buckets(self):
- self.exhaust('c', 5, username='alice')
- self.exhaust('c', 5, username='bob')
- self.exhaust('c', 5, username='chuck')
- self.exhaust('c', 0, username='chuck')
- self.exhaust('c', 0, username='bob')
- self.exhaust('c', 0, username='alice')
-
-
-class FakeLimiter(object):
- """Fake Limiter class that you can tell how to behave."""
-
- def __init__(self, test):
- self._action = self._username = self._delay = None
- self.test = test
-
- def mock(self, action, username, delay):
- self._action = action
- self._username = username
- self._delay = delay
-
- def perform(self, action, username):
- self.test.assertEqual(action, self._action)
- self.test.assertEqual(username, self._username)
- return self._delay
-
-
-class WSGIAppTest(test.TestCase):
+ """Run before each test."""
+ test.TestCase.setUp(self)
+ self.time = 0.0
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ self.limiter = limits.Limiter(TEST_LIMITS)
+
+ def tearDown(self):
+ """Run after each test."""
+ self.stubs.UnsetAll()
+
+ def _get_time(self):
+ """Return the "time" according to this test suite."""
+ return self.time
+
+ def _check(self, num, verb, url, username=None):
+ """Check and yield results from checks."""
+ for x in xrange(num):
+ yield self.limiter.check_for_delay(verb, url, username)
+
+ def _check_sum(self, num, verb, url, username=None):
+ """Check and sum results from checks."""
+ results = self._check(num, verb, url, username)
+ return sum(filter(lambda x: x != None, results))
+
+ def test_no_delay_GET(self):
+ """
+ Simple test to ensure no delay on a single call for a limit verb we
+ didn"t set.
+ """
+ delay = self.limiter.check_for_delay("GET", "/anything")
+ self.assertEqual(delay, None)
+
+ def test_no_delay_PUT(self):
+ """
+ Simple test to ensure no delay on a single call for a known limit.
+ """
+ delay = self.limiter.check_for_delay("PUT", "/anything")
+ self.assertEqual(delay, None)
+
+ def test_delay_PUT(self):
+ """
+ Ensure the 11th PUT will result in a delay of 6.0 seconds until
+ the next request will be granced.
+ """
+ expected = [None] * 10 + [6.0]
+ results = list(self._check(11, "PUT", "/anything"))
+
+ self.assertEqual(expected, results)
+
+ def test_delay_POST(self):
+ """
+ Ensure the 8th POST will result in a delay of 6.0 seconds until
+ the next request will be granced.
+ """
+ expected = [None] * 7
+ results = list(self._check(7, "POST", "/anything"))
+ self.assertEqual(expected, results)
+
+ expected = 60.0 / 7.0
+ results = self._check_sum(1, "POST", "/anything")
+ self.failUnlessAlmostEqual(expected, results, 8)
+
+ def test_delay_GET(self):
+ """
+ Ensure the 11th GET will result in NO delay.
+ """
+ expected = [None] * 11
+ results = list(self._check(11, "GET", "/anything"))
+
+ self.assertEqual(expected, results)
+
+ def test_delay_PUT_servers(self):
+ """
+ Ensure PUT on /servers limits at 5 requests, and PUT elsewhere is still
+ OK after 5 requests...but then after 11 total requests, PUT limiting
+ kicks in.
+ """
+ # First 6 requests on PUT /servers
+ expected = [None] * 5 + [12.0]
+ results = list(self._check(6, "PUT", "/servers"))
+ self.assertEqual(expected, results)
+
+ # Next 5 request on PUT /anything
+ expected = [None] * 4 + [6.0]
+ results = list(self._check(5, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ def test_delay_PUT_wait(self):
+ """
+ Ensure after hitting the limit and then waiting for the correct
+ amount of time, the limit will be lifted.
+ """
+ expected = [None] * 10 + [6.0]
+ results = list(self._check(11, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ # Advance time
+ self.time += 6.0
+
+ expected = [None, 6.0]
+ results = list(self._check(2, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ def test_multiple_delays(self):
+ """
+ Ensure multiple requests still get a delay.
+ """
+ expected = [None] * 10 + [6.0] * 10
+ results = list(self._check(20, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ self.time += 1.0
+
+ expected = [5.0] * 10
+ results = list(self._check(10, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ def test_multiple_users(self):
+ """
+ Tests involving multiple users.
+ """
+ # User1
+ expected = [None] * 10 + [6.0] * 10
+ results = list(self._check(20, "PUT", "/anything", "user1"))
+ self.assertEqual(expected, results)
+
+ # User2
+ expected = [None] * 10 + [6.0] * 5
+ results = list(self._check(15, "PUT", "/anything", "user2"))
+ self.assertEqual(expected, results)
+
+ self.time += 1.0
+
+ # User1 again
+ expected = [5.0] * 10
+ results = list(self._check(10, "PUT", "/anything", "user1"))
+ self.assertEqual(expected, results)
+
+ self.time += 1.0
+
+ # User1 again
+ expected = [4.0] * 5
+ results = list(self._check(5, "PUT", "/anything", "user2"))
+ self.assertEqual(expected, results)
+
+
+class WsgiLimiterTest(test.TestCase):
+ """
+ Tests for `limits.WsgiLimiter` class.
+ """
def setUp(self):
- super(WSGIAppTest, self).setUp()
- self.limiter = FakeLimiter(self)
- self.app = ratelimiting.WSGIApp(self.limiter)
+ """Run before each test."""
+ test.TestCase.setUp(self)
+ self.time = 0.0
+ self.app = limits.WsgiLimiter(TEST_LIMITS)
+ self.app._limiter._get_time = self._get_time
- def test_invalid_methods(self):
- requests = []
- for method in ['GET', 'PUT', 'DELETE']:
- req = webob.Request.blank('/limits/michael/breakdance',
- dict(REQUEST_METHOD=method))
- requests.append(req)
- for req in requests:
- self.assertEqual(req.get_response(self.app).status_int, 405)
-
- def test_invalid_urls(self):
- requests = []
- for prefix in ['limit', '', 'limiter2', 'limiter/limits', 'limiter/1']:
- req = webob.Request.blank('/%s/michael/breakdance' % prefix,
- dict(REQUEST_METHOD='POST'))
- requests.append(req)
- for req in requests:
- self.assertEqual(req.get_response(self.app).status_int, 404)
-
- def verify(self, url, username, action, delay=None):
+ def _get_time(self):
+ """Return the "time" according to this test suite."""
+ return self.time
+
+ def _request_data(self, verb, path):
+ """Get data decribing a limit request verb/path."""
+ return json.dumps({"verb":verb, "path":path})
+
+ def _request(self, verb, url, username=None):
"""Make sure that POSTing to the given url causes the given username
to perform the given action. Make the internal rate limiter return
delay and make sure that the WSGI app returns the correct response.
"""
- req = webob.Request.blank(url, dict(REQUEST_METHOD='POST'))
- self.limiter.mock(action, username, delay)
- resp = req.get_response(self.app)
- if not delay:
- self.assertEqual(resp.status_int, 200)
+ if username:
+ request = webob.Request.blank("/%s" % username)
else:
- self.assertEqual(resp.status_int, 403)
- self.assertEqual(resp.headers['X-Wait-Seconds'], "%.2f" % delay)
+ request = webob.Request.blank("/")
+
+ request.method = "POST"
+ request.body = self._request_data(verb, url)
+ response = request.get_response(self.app)
+
+ if "X-Wait-Seconds" in response.headers:
+ self.assertEqual(response.status_int, 403)
+ return response.headers["X-Wait-Seconds"]
+
+ self.assertEqual(response.status_int, 204)
- def test_good_urls(self):
- self.verify('/limiter/michael/hoot', 'michael', 'hoot')
+ def test_invalid_methods(self):
+ """Only POSTs should work."""
+ requests = []
+ for method in ["GET", "PUT", "DELETE", "HEAD", "OPTIONS"]:
+ request = webob.Request.blank("/")
+ request.body = self._request_data("GET", "/something")
+ response = request.get_response(self.app)
+ self.assertEqual(response.status_int, 405)
+
+ def test_good_url(self):
+ delay = self._request("GET", "/something")
+ self.assertEqual(delay, None)
def test_escaping(self):
- self.verify('/limiter/michael/jump%20up', 'michael', 'jump up')
+ delay = self._request("GET", "/something/jump%20up")
+ self.assertEqual(delay, None)
def test_response_to_delays(self):
- self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1)
- self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1.56)
- self.verify('/limiter/michael/hoot', 'michael', 'hoot', 1000)
+ delay = self._request("GET", "/delayed")
+ self.assertEqual(delay, None)
+
+ delay = self._request("GET", "/delayed")
+ self.assertEqual(delay, '60.00')
+
+ def test_response_to_delays_usernames(self):
+ delay = self._request("GET", "/delayed", "user1")
+ self.assertEqual(delay, None)
+
+ delay = self._request("GET", "/delayed", "user2")
+ self.assertEqual(delay, None)
+
+ delay = self._request("GET", "/delayed", "user1")
+ self.assertEqual(delay, '60.00')
+
+ delay = self._request("GET", "/delayed", "user2")
+ self.assertEqual(delay, '60.00')
class FakeHttplibSocket(object):
- """a fake socket implementation for httplib.HTTPResponse, trivial"""
+ """
+ Fake `httplib.HTTPResponse` replacement.
+ """
def __init__(self, response_string):
+ """Initialize new `FakeHttplibSocket`."""
self._buffer = StringIO.StringIO(response_string)
def makefile(self, _mode, _other):
- """Returns the socket's internal buffer"""
+ """Returns the socket's internal buffer."""
return self._buffer
class FakeHttplibConnection(object):
- """A fake httplib.HTTPConnection
-
- Requests made via this connection actually get translated and routed into
- our WSGI app, we then wait for the response and turn it back into
- an httplib.HTTPResponse.
"""
- def __init__(self, app, host, is_secure=False):
+ Fake `httplib.HTTPConnection`.
+ """
+
+ def __init__(self, app, host):
+ """
+ Initialize `FakeHttplibConnection`.
+ """
self.app = app
self.host = host
- def request(self, method, path, data='', headers={}):
+ def request(self, method, path, body="", headers={}):
+ """
+ Requests made via this connection actually get translated and routed into
+ our WSGI app, we then wait for the response and turn it back into
+ an `httplib.HTTPResponse`.
+ """
req = webob.Request.blank(path)
req.method = method
- req.body = data
req.headers = headers
req.host = self.host
- # Call the WSGI app, get the HTTP response
+ req.body = body
+
resp = str(req.get_response(self.app))
- # For some reason, the response doesn't have "HTTP/1.0 " prepended; I
- # guess that's a function the web server usually provides.
resp = "HTTP/1.0 %s" % resp
sock = FakeHttplibSocket(resp)
self.http_response = httplib.HTTPResponse(sock)
self.http_response.begin()
def getresponse(self):
+ """Return our generated response from the request."""
return self.http_response
@@ -208,36 +351,36 @@ def wire_HTTPConnection_to_WSGI(host, app):
httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection)
-class WSGIAppProxyTest(test.TestCase):
+class WsgiLimiterProxyTest(test.TestCase):
+ """
+ Tests for the `limits.WsgiLimiterProxy` class.
+ """
def setUp(self):
- """Our WSGIAppProxy is going to call across an HTTPConnection to a
- WSGIApp running a limiter. The proxy will send input, and the proxy
- should receive that same input, pass it to the limiter who gives a
- result, and send the expected result back.
-
- The HTTPConnection isn't real -- it's monkeypatched to point straight
- at the WSGIApp. And the limiter isn't real -- it's a fake that
- behaves the way we tell it to.
"""
- super(WSGIAppProxyTest, self).setUp()
- self.limiter = FakeLimiter(self)
- app = ratelimiting.WSGIApp(self.limiter)
- wire_HTTPConnection_to_WSGI('100.100.100.100:80', app)
- self.proxy = ratelimiting.WSGIAppProxy('100.100.100.100:80')
+ Do some nifty HTTP/WSGI magic which allows for WSGI to be called
+ directly by something like the `httplib` library.
+ """
+ test.TestCase.setUp(self)
+ self.time = 0.0
+ self.app = limits.WsgiLimiter(TEST_LIMITS)
+ self.app._limiter._get_time = self._get_time
+ wire_HTTPConnection_to_WSGI("169.254.0.1:80", self.app)
+ self.proxy = limits.WsgiLimiterProxy("169.254.0.1:80")
+
+ def _get_time(self):
+ """Return the "time" according to this test suite."""
+ return self.time
def test_200(self):
- self.limiter.mock('conquer', 'caesar', None)
- when = self.proxy.perform('conquer', 'caesar')
- self.assertEqual(when, None)
+ """Successful request test."""
+ delay = self.proxy.check_for_delay("GET", "/anything")
+ self.assertEqual(delay, None)
def test_403(self):
- self.limiter.mock('grumble', 'proletariat', 1.5)
- when = self.proxy.perform('grumble', 'proletariat')
- self.assertEqual(when, 1.5)
-
- def test_failure(self):
- def shouldRaise():
- self.limiter.mock('murder', 'brutus', None)
- self.proxy.perform('stab', 'brutus')
- self.assertRaises(AssertionError, shouldRaise)
+ """Forbidden request test."""
+ delay = self.proxy.check_for_delay("GET", "/delayed")
+ self.assertEqual(delay, None)
+
+ delay = self.proxy.check_for_delay("GET", "/delayed")
+ self.assertEqual(delay, '60.00')
--
cgit
From f1acc3d199a1a92b531a3e74ed54a8b2fcdb999c Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Tue, 15 Mar 2011 13:52:03 -0700
Subject: Now that the fix for 732866, stop working around the bug
---
nova/tests/integrated/api/client.py | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/nova/tests/integrated/api/client.py b/nova/tests/integrated/api/client.py
index 568e8c17e..fc7c344e7 100644
--- a/nova/tests/integrated/api/client.py
+++ b/nova/tests/integrated/api/client.py
@@ -108,10 +108,7 @@ class TestOpenStackClient(object):
http_status = response.status
LOG.debug(_("%(auth_uri)s => code %(http_status)s") % locals())
- # Until bug732866 is fixed, we can't check this properly...
- # bug732866
- #if http_status == 401:
- if http_status != 204:
+ if http_status == 401:
raise OpenStackApiAuthenticationException(response=response)
auth_headers = {}
--
cgit
From 0eaf02efd5fef3f77fced9c1a71c32a6f14f293f Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Tue, 15 Mar 2011 16:21:22 -0500
Subject: Add logging to lock check
---
nova/utils.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/nova/utils.py b/nova/utils.py
index 87e726394..d6f9ba829 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -518,6 +518,9 @@ def synchronized(name):
def wrap(f):
@functools.wraps(f)
def inner(*args, **kwargs):
+ LOG.debug(_("Attempting to grab %(lock)s for method "
+ "%(method)s..." % {"lock": name,
+ "method": f.__name__}))
lock = lockfile.FileLock(os.path.join(FLAGS.lock_path,
'nova-%s.lock' % name))
with lock:
--
cgit
From 1b477c2225816ea8f05595a8812932d516828e01 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Tue, 15 Mar 2011 17:47:34 -0400
Subject: pep8 fixes
---
nova/api/openstack/limits.py | 43 +++++++++++++--------------
nova/tests/api/openstack/test_ratelimiting.py | 19 ++++++------
2 files changed, 30 insertions(+), 32 deletions(-)
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index 4f64cab3c..b1e633330 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -58,12 +58,12 @@ class LimitsController(Controller):
rate_limits = req.environ.get("nova.limits", {})
return {
- "limits" : {
- "rate" : rate_limits,
- "absolute" : abs_limits,
+ "limits": {
+ "rate": rate_limits,
+ "absolute": abs_limits,
},
}
-
+
class Limit(object):
"""
@@ -71,10 +71,10 @@ class Limit(object):
"""
UNITS = {
- 1 : "SECOND",
- 60 : "MINUTE",
- 60 * 60 : "HOUR",
- 60 * 60 * 24 : "DAY",
+ 1: "SECOND",
+ 60: "MINUTE",
+ 60 * 60: "HOUR",
+ 60 * 60 * 24: "DAY",
}
def __init__(self, verb, uri, regex, value, unit):
@@ -137,14 +137,13 @@ class Limit(object):
cap = self.capacity
water = self.water_level
val = self.value
-
+
self.remaining = math.floor((cap - water) / cap * val)
- print "Remaining:", self.remaining
self.next_request = now
def _get_time(self):
"""Retrieve the current time. Broken out for testability."""
- return time.time()
+ return time.time()
def display_unit(self):
"""Display the string name of the unit."""
@@ -153,17 +152,15 @@ class Limit(object):
def display(self):
"""Return a useful representation of this class."""
return {
- "verb" : self.verb,
- "uri" : self.uri,
- "regex" : self.regex,
- "value" : self.value,
- "remaining" : int(self.remaining),
- "unit" : self.display_unit(),
- "resetTime" : int(self.next_request or self._get_time()),
+ "verb": self.verb,
+ "uri": self.uri,
+ "regex": self.regex,
+ "value": self.value,
+ "remaining": int(self.remaining),
+ "unit": self.display_unit(),
+ "resetTime": int(self.next_request or self._get_time()),
}
-
-
# "Limit" format is a dictionary with the HTTP verb, human-readable URI,
# a regular-expression to match, value and unit of measure (PER_DAY, etc.)
@@ -324,11 +321,11 @@ class WsgiLimiterProxy(object):
self.limiter_address = limiter_address
def check_for_delay(self, verb, path, username=None):
- body = json.dumps({"verb":verb,"path":path})
- headers = {"Content-Type" : "application/json"}
+ body = json.dumps({"verb": verb, "path": path})
+ headers = {"Content-Type": "application/json"}
conn = httplib.HTTPConnection(self.limiter_address)
-
+
if username:
conn.request("POST", "/%s" % (username), body, headers)
else:
diff --git a/nova/tests/api/openstack/test_ratelimiting.py b/nova/tests/api/openstack/test_ratelimiting.py
index c88de2db7..a706364b4 100644
--- a/nova/tests/api/openstack/test_ratelimiting.py
+++ b/nova/tests/api/openstack/test_ratelimiting.py
@@ -39,6 +39,7 @@ TEST_LIMITS = [
Limit("PUT", "/servers", "^/servers", 5, limits.PER_MINUTE),
]
+
class LimiterTest(test.TestCase):
"""
Tests for the in-memory `limits.Limiter` class.
@@ -107,7 +108,7 @@ class LimiterTest(test.TestCase):
expected = 60.0 / 7.0
results = self._check_sum(1, "POST", "/anything")
self.failUnlessAlmostEqual(expected, results, 8)
-
+
def test_delay_GET(self):
"""
Ensure the 11th GET will result in NO delay.
@@ -144,7 +145,7 @@ class LimiterTest(test.TestCase):
# Advance time
self.time += 6.0
-
+
expected = [None, 6.0]
results = list(self._check(2, "PUT", "/anything"))
self.assertEqual(expected, results)
@@ -190,8 +191,8 @@ class LimiterTest(test.TestCase):
expected = [4.0] * 5
results = list(self._check(5, "PUT", "/anything", "user2"))
self.assertEqual(expected, results)
-
-
+
+
class WsgiLimiterTest(test.TestCase):
"""
Tests for `limits.WsgiLimiter` class.
@@ -210,7 +211,7 @@ class WsgiLimiterTest(test.TestCase):
def _request_data(self, verb, path):
"""Get data decribing a limit request verb/path."""
- return json.dumps({"verb":verb, "path":path})
+ return json.dumps({"verb": verb, "path": path})
def _request(self, verb, url, username=None):
"""Make sure that POSTing to the given url causes the given username
@@ -221,7 +222,7 @@ class WsgiLimiterTest(test.TestCase):
request = webob.Request.blank("/%s" % username)
else:
request = webob.Request.blank("/")
-
+
request.method = "POST"
request.body = self._request_data(verb, url)
response = request.get_response(self.app)
@@ -229,7 +230,7 @@ class WsgiLimiterTest(test.TestCase):
if "X-Wait-Seconds" in response.headers:
self.assertEqual(response.status_int, 403)
return response.headers["X-Wait-Seconds"]
-
+
self.assertEqual(response.status_int, 204)
def test_invalid_methods(self):
@@ -298,8 +299,8 @@ class FakeHttplibConnection(object):
def request(self, method, path, body="", headers={}):
"""
- Requests made via this connection actually get translated and routed into
- our WSGI app, we then wait for the response and turn it back into
+ Requests made via this connection actually get translated and routed
+ into our WSGI app, we then wait for the response and turn it back into
an `httplib.HTTPResponse`.
"""
req = webob.Request.blank(path)
--
cgit
From e9ef6e04786a40d20f8022bec5d23d2e4503ce3a Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 15 Mar 2011 17:56:00 -0400
Subject: s/onset_files/injected_files/g
---
nova/api/openstack/servers.py | 16 ++++-----
nova/compute/api.py | 22 ++++++------
nova/compute/manager.py | 2 +-
nova/db/sqlalchemy/models.py | 2 +-
nova/quota.py | 30 ++++++++--------
nova/tests/api/openstack/test_servers.py | 48 ++++++++++++-------------
nova/tests/test_quota.py | 60 ++++++++++++++++----------------
nova/virt/xenapi/vmops.py | 21 +++++------
8 files changed, 101 insertions(+), 100 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index adb5c5f99..42fe13619 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -169,7 +169,7 @@ class Controller(wsgi.Controller):
metadata.append({'key': k, 'value': v})
personality = env['server'].get('personality', [])
- onset_files = self._get_onset_files(personality)
+ injected_files = self._get_injected_files(personality)
try:
instances = self.compute_api.create(
@@ -183,7 +183,7 @@ class Controller(wsgi.Controller):
key_name=key_pair['name'],
key_data=key_pair['public_key'],
metadata=metadata,
- onset_files=onset_files)
+ injected_files=injected_files)
except QuotaError as error:
self._handle_quota_error(error)
@@ -207,15 +207,15 @@ class Controller(wsgi.Controller):
else:
return self._deserialize(request.body, request.get_content_type())
- def _get_onset_files(self, personality):
+ def _get_injected_files(self, personality):
"""
- Create a list of onset files from the personality attribute
+ Create a list of injected files from the personality attribute
- At this time, onset_files must be formatted as a list of
+ At this time, injected_files must be formatted as a list of
(file_path, file_content) pairs for compatibility with the
underlying compute service.
"""
- onset_files = []
+ injected_files = []
for item in personality:
try:
path = item['path']
@@ -230,8 +230,8 @@ class Controller(wsgi.Controller):
except TypeError:
msg = 'Personality content for %s cannot be decoded' % path
raise exc.HTTPBadRequest(explanation=msg)
- onset_files.append((path, contents))
- return onset_files
+ injected_files.append((path, contents))
+ return injected_files
def _handle_quota_errors(self, error):
"""
diff --git a/nova/compute/api.py b/nova/compute/api.py
index c11059a28..32577af82 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -80,20 +80,20 @@ class API(base.Base):
topic,
{"method": "get_network_topic", "args": {'fake': 1}})
- def _check_onset_file_quota(self, context, onset_files):
+ def _check_injected_file_quota(self, context, injected_files):
"""
- Enforce quota limits on onset files
+ Enforce quota limits on injected files
Raises a QuotaError if any limit is exceeded
"""
- if onset_files is None:
+ if injected_files is None:
return
- limit = quota.allowed_onset_files(context)
- if len(onset_files) > limit:
+ limit = quota.allowed_injected_files(context)
+ if len(injected_files) > limit:
raise quota.QuotaError(code="OnsetFileLimitExceeded")
- path_limit = quota.allowed_onset_file_path_bytes(context)
- content_limit = quota.allowed_onset_file_content_bytes(context)
- for path, content in onset_files:
+ path_limit = quota.allowed_injected_file_path_bytes(context)
+ content_limit = quota.allowed_injected_file_content_bytes(context)
+ for path, content in injected_files:
if len(path) > path_limit:
raise quota.QuotaError(code="OnsetFilePathLimitExceeded")
if len(content) > content_limit:
@@ -105,7 +105,7 @@ class API(base.Base):
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata=[],
- onset_files=None):
+ injected_files=None):
"""Create the number of instances requested if quota and
other arguments check out ok."""
@@ -143,7 +143,7 @@ class API(base.Base):
LOG.warn(msg)
raise quota.QuotaError(msg, "MetadataLimitExceeded")
- self._check_onset_file_quota(context, onset_files)
+ self._check_injected_file_quota(context, injected_files)
image = self.image_service.show(context, image_id)
@@ -246,7 +246,7 @@ class API(base.Base):
"args": {"topic": FLAGS.compute_topic,
"instance_id": instance_id,
"availability_zone": availability_zone,
- "onset_files": onset_files}})
+ "injected_files": injected_files}})
for group_id in security_groups:
self.trigger_security_group_members_refresh(elevated, group_id)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 6bb169fa5..92deca813 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -179,7 +179,7 @@ class ComputeManager(manager.Manager):
"""Launch a new instance with specified options."""
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
- instance_ref.onset_files = kwargs.get('onset_files', [])
+ instance_ref.injected_files = kwargs.get('injected_files', [])
if instance_ref['name'] in self.driver.list_instances():
raise exception.Error(_("Instance has already been created"))
LOG.audit(_("instance %s: starting..."), instance_id,
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 162f6fded..1845e85eb 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -161,7 +161,7 @@ class Certificate(BASE, NovaBase):
class Instance(BASE, NovaBase):
"""Represents a guest vm."""
__tablename__ = 'instances'
- onset_files = []
+ injected_files = []
id = Column(Integer, primary_key=True, autoincrement=True)
diff --git a/nova/quota.py b/nova/quota.py
index e0fb97542..2b24c0b5b 100644
--- a/nova/quota.py
+++ b/nova/quota.py
@@ -37,12 +37,12 @@ flags.DEFINE_integer('quota_floating_ips', 10,
'number of floating ips allowed per project')
flags.DEFINE_integer('quota_metadata_items', 128,
'number of metadata items allowed per instance')
-flags.DEFINE_integer('quota_max_onset_files', 5,
- 'number of onset files allowed')
-flags.DEFINE_integer('quota_max_onset_file_content_bytes', 10 * 1024,
- 'number of bytes allowed per onset file')
-flags.DEFINE_integer('quota_max_onset_file_path_bytes', 255,
- 'number of bytes allowed per onset file path')
+flags.DEFINE_integer('quota_max_injected_files', 5,
+ 'number of injected files allowed')
+flags.DEFINE_integer('quota_max_injected_file_content_bytes', 10 * 1024,
+ 'number of bytes allowed per injected file')
+flags.DEFINE_integer('quota_max_injected_file_path_bytes', 255,
+ 'number of bytes allowed per injected file path')
def get_quota(context, project_id):
@@ -113,19 +113,19 @@ def allowed_metadata_items(context, num_metadata_items):
return min(num_metadata_items, num_allowed_metadata_items)
-def allowed_onset_files(context):
- """Return the number of onset files allowed"""
- return int(FLAGS.quota_max_onset_files)
+def allowed_injected_files(context):
+ """Return the number of injected files allowed"""
+ return FLAGS.quota_max_injected_files
-def allowed_onset_file_content_bytes(context):
- """Return the number of bytes allowed per onset file content"""
- return int(FLAGS.quota_max_onset_file_content_bytes)
+def allowed_injected_file_content_bytes(context):
+ """Return the number of bytes allowed per injected file content"""
+ return FLAGS.quota_max_injected_file_content_bytes
-def allowed_onset_file_path_bytes(context):
- """Return the number of bytes allowed in an onset file path"""
- return int(FLAGS.quota_max_onset_file_path_bytes)
+def allowed_injected_file_path_bytes(context):
+ """Return the number of bytes allowed in an injected file path"""
+ return FLAGS.quota_max_injected_file_path_bytes
class QuotaError(exception.ApiError):
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 7027c7ea3..253b84be9 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -834,13 +834,13 @@ class TestServerInstanceCreation(test.TestCase):
class MockComputeAPI(object):
def __init__(self):
- self.onset_files = None
+ self.injected_files = None
def create(self, *args, **kwargs):
- if 'onset_files' in kwargs:
- self.onset_files = kwargs['onset_files']
+ if 'injected_files' in kwargs:
+ self.injected_files = kwargs['injected_files']
else:
- self.onset_files = None
+ self.injected_files = None
return [{'id': '1234', 'display_name': 'fakeinstance'}]
def set_admin_password(self, *args, **kwargs):
@@ -920,46 +920,46 @@ class TestServerInstanceCreation(test.TestCase):
request = self._get_create_request_json(body_dict)
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
- return request, response, compute_api.onset_files
+ return request, response, compute_api.injected_files
def _create_instance_with_personality_xml(self, personality):
body_dict = self._create_personality_request_dict(personality)
request = self._get_create_request_xml(body_dict)
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
- return request, response, compute_api.onset_files
+ return request, response, compute_api.injected_files
def test_create_instance_with_no_personality(self):
- request, response, onset_files = \
+ request, response, injected_files = \
self._create_instance_with_personality_json(personality=None)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [])
+ self.assertEquals(injected_files, [])
def test_create_instance_with_no_personality_xml(self):
- request, response, onset_files = \
+ request, response, injected_files = \
self._create_instance_with_personality_xml(personality=None)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [])
+ self.assertEquals(injected_files, [])
def test_create_instance_with_personality(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Hello, World!"\n'
b64contents = base64.b64encode(contents)
personality = [(path, b64contents)]
- request, response, onset_files = \
+ request, response, injected_files = \
self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [(path, contents)])
+ self.assertEquals(injected_files, [(path, contents)])
def test_create_instance_with_personality_xml(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Hello, World!"\n'
b64contents = base64.b64encode(contents)
personality = [(path, b64contents)]
- request, response, onset_files = \
+ request, response, injected_files = \
self._create_instance_with_personality_xml(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [(path, contents)])
+ self.assertEquals(injected_files, [(path, contents)])
def test_create_instance_with_personality_no_path(self):
personality = [('/remove/this/path',
@@ -970,7 +970,7 @@ class TestServerInstanceCreation(test.TestCase):
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.onset_files, None)
+ self.assertEquals(compute_api.injected_files, None)
def _test_create_instance_with_personality_no_path_xml(self):
personality = [('/remove/this/path',
@@ -981,7 +981,7 @@ class TestServerInstanceCreation(test.TestCase):
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.onset_files, None)
+ self.assertEquals(compute_api.injected_files, None)
def test_create_instance_with_personality_no_contents(self):
personality = [('/test/path',
@@ -992,7 +992,7 @@ class TestServerInstanceCreation(test.TestCase):
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.onset_files, None)
+ self.assertEquals(compute_api.injected_files, None)
def test_create_instance_with_personality_not_a_list(self):
personality = [('/test/path', base64.b64encode('test\ncontents\n'))]
@@ -1003,16 +1003,16 @@ class TestServerInstanceCreation(test.TestCase):
compute_api, response = \
self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 400)
- self.assertEquals(compute_api.onset_files, None)
+ self.assertEquals(compute_api.injected_files, None)
def test_create_instance_with_personality_with_non_b64_content(self):
path = '/my/file/path'
contents = '#!/bin/bash\necho "Oh no!"\n'
personality = [(path, contents)]
- request, response, onset_files = \
+ request, response, injected_files = \
self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 400)
- self.assertEquals(onset_files, None)
+ self.assertEquals(injected_files, None)
def test_create_instance_with_three_personalities(self):
files = [
@@ -1023,19 +1023,19 @@ class TestServerInstanceCreation(test.TestCase):
personality = []
for path, content in files:
personality.append((path, base64.b64encode(content)))
- request, response, onset_files = \
+ request, response, injected_files = \
self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, files)
+ self.assertEquals(injected_files, files)
def test_create_instance_personality_empty_content(self):
path = '/my/file/path'
contents = ''
personality = [(path, contents)]
- request, response, onset_files = \
+ request, response, injected_files = \
self._create_instance_with_personality_json(personality)
self.assertEquals(response.status_int, 200)
- self.assertEquals(onset_files, [(path, contents)])
+ self.assertEquals(injected_files, [(path, contents)])
def test_create_instance_admin_pass_json(self):
request, response, dummy = \
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index d94381aa2..c65bc459d 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -200,66 +200,66 @@ class QuotaTestCase(test.TestCase):
image_id='fake',
metadata=metadata)
- def test_allowed_onset_files(self):
+ def test_allowed_injected_files(self):
self.assertEqual(
- quota.allowed_onset_files(self.context),
- FLAGS.quota_max_onset_files)
+ quota.allowed_injected_files(self.context),
+ FLAGS.quota_max_injected_files)
- def _create_with_onset_files(self, files):
+ def _create_with_injected_files(self, files):
api = compute.API(image_service=self.StubImageService())
api.create(self.context, min_count=1, max_count=1,
instance_type='m1.small', image_id='fake',
- onset_files=files)
+ injected_files=files)
- def test_no_onset_files(self):
+ def test_no_injected_files(self):
api = compute.API(image_service=self.StubImageService())
api.create(self.context, instance_type='m1.small', image_id='fake')
- def test_max_onset_files(self):
+ def test_max_injected_files(self):
files = []
- for i in xrange(FLAGS.quota_max_onset_files):
+ for i in xrange(FLAGS.quota_max_injected_files):
files.append(('/my/path%d' % i, 'config = test\n'))
- self._create_with_onset_files(files) # no QuotaError
+ self._create_with_injected_files(files) # no QuotaError
- def test_too_many_onset_files(self):
+ def test_too_many_injected_files(self):
files = []
- for i in xrange(FLAGS.quota_max_onset_files + 1):
+ for i in xrange(FLAGS.quota_max_injected_files + 1):
files.append(('/my/path%d' % i, 'my\ncontent%d\n' % i))
self.assertRaises(quota.QuotaError,
- self._create_with_onset_files, files)
+ self._create_with_injected_files, files)
- def test_allowed_onset_file_content_bytes(self):
+ def test_allowed_injected_file_content_bytes(self):
self.assertEqual(
- quota.allowed_onset_file_content_bytes(self.context),
- FLAGS.quota_max_onset_file_content_bytes)
+ quota.allowed_injected_file_content_bytes(self.context),
+ FLAGS.quota_max_injected_file_content_bytes)
- def test_max_onset_file_content_bytes(self):
- max = FLAGS.quota_max_onset_file_content_bytes
+ def test_max_injected_file_content_bytes(self):
+ max = FLAGS.quota_max_injected_file_content_bytes
content = ''.join(['a' for i in xrange(max)])
files = [('/test/path', content)]
- self._create_with_onset_files(files) # no QuotaError
+ self._create_with_injected_files(files) # no QuotaError
- def test_too_many_onset_file_content_bytes(self):
- max = FLAGS.quota_max_onset_file_content_bytes
+ def test_too_many_injected_file_content_bytes(self):
+ max = FLAGS.quota_max_injected_file_content_bytes
content = ''.join(['a' for i in xrange(max + 1)])
files = [('/test/path', content)]
self.assertRaises(quota.QuotaError,
- self._create_with_onset_files, files)
+ self._create_with_injected_files, files)
- def test_allowed_onset_file_path_bytes(self):
+ def test_allowed_injected_file_path_bytes(self):
self.assertEqual(
- quota.allowed_onset_file_path_bytes(self.context),
- FLAGS.quota_max_onset_file_path_bytes)
+ quota.allowed_injected_file_path_bytes(self.context),
+ FLAGS.quota_max_injected_file_path_bytes)
- def test_max_onset_file_path_bytes(self):
- max = FLAGS.quota_max_onset_file_path_bytes
+ def test_max_injected_file_path_bytes(self):
+ max = FLAGS.quota_max_injected_file_path_bytes
path = ''.join(['a' for i in xrange(max)])
files = [(path, 'config = quotatest')]
- self._create_with_onset_files(files) # no QuotaError
+ self._create_with_injected_files(files) # no QuotaError
- def test_too_many_onset_file_path_bytes(self):
- max = FLAGS.quota_max_onset_file_path_bytes
+ def test_too_many_injected_file_path_bytes(self):
+ max = FLAGS.quota_max_injected_file_path_bytes
path = ''.join(['a' for i in xrange(max + 1)])
files = [(path, 'config = quotatest')]
self.assertRaises(quota.QuotaError,
- self._create_with_onset_files, files)
+ self._create_with_injected_files, files)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index d3fc335fe..488a61e8e 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -137,19 +137,20 @@ class VMOps(object):
LOG.info(_('Spawning VM %(instance_name)s created %(vm_ref)s.')
% locals())
- def _inject_onset_files():
- onset_files = instance.onset_files
- if onset_files:
+ def _inject_files():
+ injected_files = instance.injected_files
+ if injected_files:
# Check if this is a JSON-encoded string and convert if needed.
- if isinstance(onset_files, basestring):
+ if isinstance(injected_files, basestring):
try:
- onset_files = json.loads(onset_files)
+ injected_files = json.loads(injected_files)
except ValueError:
- LOG.exception(_("Invalid value for onset_files: '%s'")
- % onset_files)
- onset_files = []
+ LOG.exception(
+ _("Invalid value for injected_files: '%s'")
+ % injected_files)
+ injected_files = []
# Inject any files, if specified
- for path, contents in instance.onset_files:
+ for path, contents in instance.injected_files:
LOG.debug(_("Injecting file path: '%s'") % path)
self.inject_file(instance, path, contents)
# NOTE(armando): Do we really need to do this in virt?
@@ -165,7 +166,7 @@ class VMOps(object):
if state == power_state.RUNNING:
LOG.debug(_('Instance %s: booted'), instance_name)
timer.stop()
- _inject_onset_files()
+ _inject_files()
return True
except Exception, exc:
LOG.warn(exc)
--
cgit
From 74068a7b504a95dc8e0339faa04c8c5520417f32 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Tue, 15 Mar 2011 18:10:25 -0400
Subject: Per Eric Day's suggest, the verson is not store in the request
environ instead of the nova.context.
---
nova/api/openstack/auth.py | 4 ++--
nova/api/openstack/views/addresses.py | 2 +-
nova/api/openstack/views/flavors.py | 2 +-
nova/api/openstack/views/images.py | 2 +-
nova/api/openstack/views/servers.py | 2 +-
nova/context.py | 7 ++-----
nova/tests/api/openstack/fakes.py | 4 +++-
nova/tests/api/openstack/test_servers.py | 16 ++--------------
8 files changed, 13 insertions(+), 26 deletions(-)
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index e33a9faf5..c820a5963 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -68,9 +68,9 @@ class AuthMiddleware(wsgi.Middleware):
not self.auth.is_project_member(user, account):
return faults.Fault(webob.exc.HTTPUnauthorized())
+ req.environ['nova.context'] = context.RequestContext(user, account)
version = req.path.split('/')[1].replace('v', '')
- req.environ['nova.context'] = context.RequestContext(user, account,
- version=version)
+ req.environ['version'] = version
return self.application
def has_authentication(self, req):
diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py
index d764e5229..65c24dbd7 100644
--- a/nova/api/openstack/views/addresses.py
+++ b/nova/api/openstack/views/addresses.py
@@ -8,7 +8,7 @@ def get_view_builder(req):
A factory method that returns the correct builder based on the version of
the api requested.
'''
- version = req.environ['nova.context'].version
+ version = req.environ['version']
if version == '1.1':
return ViewBuilder_1_1()
else:
diff --git a/nova/api/openstack/views/flavors.py b/nova/api/openstack/views/flavors.py
index dfcc2644c..f945f9f8f 100644
--- a/nova/api/openstack/views/flavors.py
+++ b/nova/api/openstack/views/flavors.py
@@ -5,7 +5,7 @@ def get_view_builder(req):
A factory method that returns the correct builder based on the version of
the api requested.
'''
- version = req.environ['nova.context'].version
+ version = req.environ['version']
base_url = req.application_url
if version == '1.1':
return ViewBuilder_1_1(base_url)
diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py
index cd61ed656..a59d4a557 100644
--- a/nova/api/openstack/views/images.py
+++ b/nova/api/openstack/views/images.py
@@ -5,7 +5,7 @@ def get_view_builder(req):
A factory method that returns the correct builder based on the version of
the api requested.
'''
- version = req.environ['nova.context'].version
+ version = req.environ['version']
base_url = req.application_url
if version == '1.1':
return ViewBuilder_1_1(base_url)
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 7ca2b2427..2549cc11c 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -11,7 +11,7 @@ def get_view_builder(req):
A factory method that returns the correct builder based on the version of
the api requested.
'''
- version = req.environ['nova.context'].version
+ version = req.environ['version']
addresses_builder = addresses_view.get_view_builder(req)
if version == '1.1':
flavor_builder = flavors_view.get_view_builder(req)
diff --git a/nova/context.py b/nova/context.py
index 0f3eb9ae4..0256bf448 100644
--- a/nova/context.py
+++ b/nova/context.py
@@ -29,8 +29,7 @@ from nova import utils
class RequestContext(object):
def __init__(self, user, project, is_admin=None, read_deleted=False,
- remote_address=None, timestamp=None, request_id=None,
- version=None):
+ remote_address=None, timestamp=None, request_id=None):
if hasattr(user, 'id'):
self._user = user
self.user_id = user.id
@@ -61,7 +60,6 @@ class RequestContext(object):
chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-'
request_id = ''.join([random.choice(chars) for x in xrange(20)])
self.request_id = request_id
- self.version = version
@property
def user(self):
@@ -95,8 +93,7 @@ class RequestContext(object):
'read_deleted': self.read_deleted,
'remote_address': self.remote_address,
'timestamp': utils.isotime(self.timestamp),
- 'request_id': self.request_id,
- 'version': self.version}
+ 'request_id': self.request_id}
@classmethod
def from_dict(cls, values):
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 9f8ee9b56..9c3b53ac7 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -68,7 +68,9 @@ def fake_auth_init(self, application):
@webob.dec.wsgify
def fake_wsgi(self, req):
- req.environ['nova.context'] = context.RequestContext(1, 1, version='1.0')
+ req.environ['nova.context'] = context.RequestContext(1, 1)
+ if not req.environ.get('version'):
+ req.environ['version'] = '1.0'
if req.body:
req.environ['inst_dict'] = json.loads(req.body)
return self.application
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index ac115ebf7..6b804d3b4 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -178,17 +178,12 @@ class ServersTest(test.TestCase):
self.assertEqual(addresses["private"][0], private)
def test_get_server_by_id_with_addresses_v1_1(self):
- class FakeRequestContext(object):
- def __init__(self, user, project, *args, **kwargs):
- self.user_id = 1
- self.project_id = 1
- self.version = '1.1'
- self.stubs.Set(context, 'RequestContext', FakeRequestContext)
private = "192.168.0.3"
public = ["1.2.3.4"]
new_return_server = return_server_with_addresses(private, public)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
req = webob.Request.blank('/v1.1/servers/1')
+ req.environ['version'] = '1.1'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['server']['id'], '1')
@@ -367,15 +362,8 @@ class ServersTest(test.TestCase):
self.assertEqual(s['metadata']['seq'], i)
def test_get_all_server_details_v1_1(self):
- class FakeRequestContext(object):
- def __init__(self, user, project, *args, **kwargs):
- self.user_id = 1
- self.project_id = 1
- self.version = '1.1'
- self.is_admin = True
-
- self.stubs.Set(context, 'RequestContext', FakeRequestContext)
req = webob.Request.blank('/v1.1/servers/detail')
+ req.environ['version'] = '1.1'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
--
cgit
From 3cc78174e023b3f848b9c4b30468d356ee575ea6 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 15 Mar 2011 18:11:54 -0400
Subject: internationalization
---
nova/api/openstack/servers.py | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 42fe13619..f618c31a0 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -221,15 +221,16 @@ class Controller(wsgi.Controller):
path = item['path']
contents = item['contents']
except KeyError as key:
- expl = 'Bad personality format: missing %s' % key
+ expl = _('Bad personality format: missing %s') % key
raise exc.HTTPBadRequest(explanation=expl)
except TypeError:
- raise exc.HTTPBadRequest(explanation='Bad personality format')
+ expl = _('Bad personality format')
+ raise exc.HTTPBadRequest(explanation=expl)
try:
contents = base64.b64decode(contents)
except TypeError:
- msg = 'Personality content for %s cannot be decoded' % path
- raise exc.HTTPBadRequest(explanation=msg)
+ expl = _('Personality content for %s cannot be decoded') % path
+ raise exc.HTTPBadRequest(explanation=expl)
injected_files.append((path, contents))
return injected_files
@@ -238,13 +239,13 @@ class Controller(wsgi.Controller):
Reraise quota errors as api-specific http exceptions
"""
if error.code == "OnsetFileLimitExceeded":
- expl = "Personality file limit exceeded"
+ expl = _("Personality file limit exceeded")
raise exc.HTTPBadRequest(explanation=expl)
if error.code == "OnsetFilePathLimitExceeded":
- expl = "Personality file path too long"
+ expl = _("Personality file path too long")
raise exc.HTTPBadRequest(explanation=expl)
if error.code == "OnsetFileContentLimitExceeded":
- expl = "Personality file content too long"
+ expl = _("Personality file content too long")
raise exc.HTTPBadRequest(explanation=expl)
# if the original error is okay, just reraise it
raise error
--
cgit
From 70769dbe239c979d97154b88a33cb34d377d1196 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 15 Mar 2011 18:12:46 -0400
Subject: pep8
---
nova/tests/api/openstack/test_servers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 253b84be9..a92c0f590 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -806,7 +806,7 @@ ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv\
dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy\
c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6\
b25zLiINCg0KLVJpY2hhcmQgQmFjaA==""",
- }
+ },
],
}}
request = self.deserializer.deserialize(serial_request)
--
cgit
From 6d984c3097252f9f97ef10e48be390fdf756b391 Mon Sep 17 00:00:00 2001
From: Ken Pepple
Date: Tue, 15 Mar 2011 16:08:22 -0700
Subject: wrap errors getting image ids from local image store
---
nova/image/local.py | 14 ++++++++++++--
nova/tests/api/openstack/test_images.py | 7 +++++++
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/nova/image/local.py b/nova/image/local.py
index c4ac3baaa..ef92a35b5 100644
--- a/nova/image/local.py
+++ b/nova/image/local.py
@@ -20,8 +20,9 @@ import os.path
import random
import shutil
-from nova import flags
from nova import exception
+from nova import flags
+from nova import log as logging
from nova.image import service
@@ -29,6 +30,8 @@ FLAGS = flags.FLAGS
flags.DEFINE_string('images_path', '$state_path/images',
'path to decrypted images')
+LOG = logging.getLogger('nova.image.local')
+
class LocalImageService(service.BaseImageService):
"""Image service storing images to local disk.
@@ -47,7 +50,14 @@ class LocalImageService(service.BaseImageService):
def _ids(self):
"""The list of all image ids."""
- return [int(i, 16) for i in os.listdir(self._path)]
+ images = []
+ for i in os.listdir(self._path):
+ try:
+ images.append(int(i, 16))
+ except:
+ LOG.debug(
+ _("%s is not in correct directory naming format" % i))
+ return images
def index(self, context):
return [dict(image_id=i['id'], name=i.get('name'))
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 76f758929..2c4918117 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -151,6 +151,13 @@ class LocalImageServiceTest(test.TestCase,
self.stubs.UnsetAll()
super(LocalImageServiceTest, self).tearDown()
+ def test_get_all_ids_with_incorrect_directory_formats(self):
+ # create some old-style image directories (starting with 'ami-')
+ for x in [1, 2, 3]:
+ tempfile.mkstemp(prefix='ami-', dir=self.tempdir)
+ found_images = self.service._ids()
+ self.assertEqual(True, isinstance(found_images, list))
+
class GlanceImageServiceTest(test.TestCase,
BaseImageServiceTests):
--
cgit
From e237b4a5653384688b16f7fd2c0708eaec4b9ec7 Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 15 Mar 2011 19:11:21 -0400
Subject: ignore differently-named nodes in personality and metadata parsing
---
nova/api/openstack/servers.py | 10 +-
nova/tests/api/openstack/test_servers.py | 164 ++++++++++++++++++++++---------
2 files changed, 126 insertions(+), 48 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index f618c31a0..ea88f1fdc 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -575,7 +575,7 @@ class ServerCreateRequestXMLDeserializer(object):
if metadata_node is None:
return None
metadata = {}
- for meta_node in metadata_node.childNodes:
+ for meta_node in self._find_children_named(metadata_node, "meta"):
key = meta_node.getAttribute("key")
metadata[key] = self._extract_text(meta_node)
return metadata
@@ -587,7 +587,7 @@ class ServerCreateRequestXMLDeserializer(object):
if personality_node is None:
return None
personality = []
- for file_node in personality_node.childNodes:
+ for file_node in self._find_children_named(personality_node, "file"):
item = {}
if file_node.hasAttribute("path"):
item["path"] = file_node.getAttribute("path")
@@ -602,6 +602,12 @@ class ServerCreateRequestXMLDeserializer(object):
return node
return None
+ def _find_children_named(self, parent, name):
+ """Return all of a nodes children who have the given name"""
+ for node in parent.childNodes:
+ if node.nodeName == name:
+ yield node
+
def _extract_text(self, node):
"""Get the text field contained by the given node"""
if len(node.childNodes) == 1:
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index a92c0f590..ed37cb705 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -610,7 +610,7 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_minimal_request(self):
serial_request = """
-"""
request = self.deserializer.deserialize(serial_request)
expected = {"server": {
@@ -622,8 +622,10 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_request_with_empty_metadata(self):
serial_request = """
-"""
+
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"server": {
"name": "new-server-test",
@@ -635,8 +637,10 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_request_with_empty_personality(self):
serial_request = """
-"""
+
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"server": {
"name": "new-server-test",
@@ -648,9 +652,11 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_request_with_empty_metadata_and_personality(self):
serial_request = """
-\
-"""
+
+
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"server": {
"name": "new-server-test",
@@ -663,9 +669,11 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_request_with_empty_metadata_and_personality_reversed(self):
serial_request = """
-\
-"""
+
+
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"server": {
"name": "new-server-test",
@@ -678,28 +686,47 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_request_with_one_personality(self):
serial_request = """
-\
-aabbccdd"""
+
+
+ aabbccdd
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = [{"path": "/etc/conf", "contents": "aabbccdd"}]
self.assertEquals(request["server"]["personality"], expected)
def test_request_with_two_personalities(self):
serial_request = """
-\
-aabbccdd\
+
+aabbccdd
abcd"""
request = self.deserializer.deserialize(serial_request)
expected = [{"path": "/etc/conf", "contents": "aabbccdd"},
{"path": "/etc/sudoers", "contents": "abcd"}]
self.assertEquals(request["server"]["personality"], expected)
+ def test_request_second_personality_node_ignored(self):
+ serial_request = """
+
+
+ aabbccdd
+
+
+ anything
+
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = [{"path": "/etc/conf", "contents": "aabbccdd"}]
+ self.assertEquals(request["server"]["personality"], expected)
+
+
def test_request_with_one_personality_missing_path(self):
serial_request = """
-\
+
aabbccdd"""
request = self.deserializer.deserialize(serial_request)
expected = [{"contents": "aabbccdd"}]
@@ -707,8 +734,8 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_request_with_one_personality_empty_contents(self):
serial_request = """
-\
+
"""
request = self.deserializer.deserialize(serial_request)
expected = [{"path": "/etc/conf", "contents": ""}]
@@ -716,8 +743,8 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_request_with_one_personality_empty_contents_variation(self):
serial_request = """
-\
+
"""
request = self.deserializer.deserialize(serial_request)
expected = [{"path": "/etc/conf", "contents": ""}]
@@ -725,57 +752,101 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
def test_request_with_one_metadata(self):
serial_request = """
-\
-beta"""
+
+
+ beta
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"alpha": "beta"}
self.assertEquals(request["server"]["metadata"], expected)
def test_request_with_two_metadata(self):
serial_request = """
-\
-betabar\
-"""
+
+
+ beta
+ bar
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"alpha": "beta", "foo": "bar"}
self.assertEquals(request["server"]["metadata"], expected)
def test_request_with_metadata_missing_value(self):
serial_request = """
-\
-"""
+
+
+
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"alpha": ""}
self.assertEquals(request["server"]["metadata"], expected)
+ def test_request_with_two_metadata_missing_value(self):
+ serial_request = """
+
+
+
+
+
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"alpha": "", "delta": ""}
+ self.assertEquals(request["server"]["metadata"], expected)
+
def test_request_with_metadata_missing_key(self):
serial_request = """
-\
-beta"""
+
+
+ beta
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"": "beta"}
self.assertEquals(request["server"]["metadata"], expected)
+ def test_request_with_two_metadata_missing_key(self):
+ serial_request = """
+
+
+ beta
+ gamma
+
+"""
+ request = self.deserializer.deserialize(serial_request)
+ expected = {"":"gamma"}
+ self.assertEquals(request["server"]["metadata"], expected)
+
def test_request_with_metadata_duplicate_key(self):
serial_request = """
-\
-barbaz\
-"""
+
+
+ bar
+ baz
+
+"""
request = self.deserializer.deserialize(serial_request)
expected = {"foo": "baz"}
self.assertEquals(request["server"]["metadata"], expected)
def test_canonical_request_from_docs(self):
serial_request = """
-\
-Apache1\
-\
+
+
+ Apache1
+
+
+ \
ICAgICAgDQoiQSBjbG91ZCBkb2VzIG5vdCBrbm93IHdoeSBp\
dCBtb3ZlcyBpbiBqdXN0IHN1Y2ggYSBkaXJlY3Rpb24gYW5k\
IGF0IHN1Y2ggYSBzcGVlZC4uLkl0IGZlZWxzIGFuIGltcHVs\
@@ -784,8 +855,9 @@ QnV0IHRoZSBza3kga25vd3MgdGhlIHJlYXNvbnMgYW5kIHRo\
ZSBwYXR0ZXJucyBiZWhpbmQgYWxsIGNsb3VkcywgYW5kIHlv\
dSB3aWxsIGtub3csIHRvbywgd2hlbiB5b3UgbGlmdCB5b3Vy\
c2VsZiBoaWdoIGVub3VnaCB0byBzZWUgYmV5b25kIGhvcml6\
-b25zLiINCg0KLVJpY2hhcmQgQmFjaA==\
-"""
+b25zLiINCg0KLVJpY2hhcmQgQmFjaA==
+
+"""
expected = {"server": {
"name": "new-server-test",
"imageId": "1",
--
cgit
From fc07caece79e379b6d6f2a3220806af9271e349b Mon Sep 17 00:00:00 2001
From: Mark Washenberger
Date: Tue, 15 Mar 2011 19:23:46 -0400
Subject: pep8
---
nova/tests/api/openstack/test_servers.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index ed37cb705..9a6f2c052 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -722,7 +722,6 @@ class TestServerCreateRequestXMLDeserializer(unittest.TestCase):
expected = [{"path": "/etc/conf", "contents": "aabbccdd"}]
self.assertEquals(request["server"]["personality"], expected)
-
def test_request_with_one_personality_missing_path(self):
serial_request = """
"""
request = self.deserializer.deserialize(serial_request)
- expected = {"":"gamma"}
+ expected = {"": "gamma"}
self.assertEquals(request["server"]["metadata"], expected)
def test_request_with_metadata_duplicate_key(self):
--
cgit
From bee1951ac78688e49939aee4e2285ef0ff89adb2 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Tue, 15 Mar 2011 19:55:13 -0400
Subject: As suggested by Eric Day: * changed request.environ version key to
more descriptive 'api.version' * removed python3 string formatting * added
licenses to headers on new files
---
nova/api/openstack/auth.py | 2 +-
nova/api/openstack/common.py | 3 +++
nova/api/openstack/views/addresses.py | 22 +++++++++++++++++++---
nova/api/openstack/views/flavors.py | 23 ++++++++++++++++++++---
nova/api/openstack/views/images.py | 23 ++++++++++++++++++++---
nova/api/openstack/views/servers.py | 20 +++++++++++++++++++-
nova/tests/api/openstack/fakes.py | 4 ++--
nova/tests/api/openstack/test_servers.py | 4 ++--
8 files changed, 86 insertions(+), 15 deletions(-)
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index c820a5963..7ae285019 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -70,7 +70,7 @@ class AuthMiddleware(wsgi.Middleware):
req.environ['nova.context'] = context.RequestContext(user, account)
version = req.path.split('/')[1].replace('v', '')
- req.environ['version'] = version
+ req.environ['nova.api.openstack.version'] = version
return self.application
def has_authentication(self, req):
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index 74ac21024..d94969ff5 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -74,3 +74,6 @@ def get_image_id_from_image_hash(image_service, context, image_hash):
if abs(hash(image_id)) == int(image_hash):
return image_id
raise exception.NotFound(image_hash)
+
+def get_api_version(req):
+ return req.environ.get('api.version')
diff --git a/nova/api/openstack/views/addresses.py b/nova/api/openstack/views/addresses.py
index 65c24dbd7..9d392aace 100644
--- a/nova/api/openstack/views/addresses.py
+++ b/nova/api/openstack/views/addresses.py
@@ -1,6 +1,22 @@
-import hashlib
-from nova.compute import power_state
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010-2011 OpenStack LLC.
+# 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.
+
from nova import utils
+from nova.api.openstack import common
def get_view_builder(req):
@@ -8,7 +24,7 @@ def get_view_builder(req):
A factory method that returns the correct builder based on the version of
the api requested.
'''
- version = req.environ['version']
+ version = common.get_api_version(req)
if version == '1.1':
return ViewBuilder_1_1()
else:
diff --git a/nova/api/openstack/views/flavors.py b/nova/api/openstack/views/flavors.py
index f945f9f8f..aa3c2aeb2 100644
--- a/nova/api/openstack/views/flavors.py
+++ b/nova/api/openstack/views/flavors.py
@@ -1,11 +1,28 @@
-
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010-2011 OpenStack LLC.
+# 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.
+
+from nova.api.openstack import common
def get_view_builder(req):
'''
A factory method that returns the correct builder based on the version of
the api requested.
'''
- version = req.environ['version']
+ version = common.get_api_version(req)
base_url = req.application_url
if version == '1.1':
return ViewBuilder_1_1(base_url)
@@ -26,7 +43,7 @@ class ViewBuilder_1_1(ViewBuilder):
self.base_url = base_url
def generate_href(self, flavor_id):
- return "{0}/flavors/{1}".format(self.base_url, flavor_id)
+ return "%s/flavors/%s" % (self.base_url, flavor_id)
class ViewBuilder_1_0(ViewBuilder):
diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py
index a59d4a557..930b464b0 100644
--- a/nova/api/openstack/views/images.py
+++ b/nova/api/openstack/views/images.py
@@ -1,11 +1,28 @@
-
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010-2011 OpenStack LLC.
+# 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.
+
+from nova.api.openstack import common
def get_view_builder(req):
'''
A factory method that returns the correct builder based on the version of
the api requested.
'''
- version = req.environ['version']
+ version = common.get_api_version(req)
base_url = req.application_url
if version == '1.1':
return ViewBuilder_1_1(base_url)
@@ -26,7 +43,7 @@ class ViewBuilder_1_1(ViewBuilder):
self.base_url = base_url
def generate_href(self, image_id):
- return "{0}/images/{1}".format(self.base_url, image_id)
+ return "%s/images/%s" % (self.base_url, image_id)
class ViewBuilder_1_0(ViewBuilder):
diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py
index 2549cc11c..261acfed0 100644
--- a/nova/api/openstack/views/servers.py
+++ b/nova/api/openstack/views/servers.py
@@ -1,5 +1,23 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010-2011 OpenStack LLC.
+# 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 hashlib
from nova.compute import power_state
+from nova.api.openstack import common
from nova.api.openstack.views import addresses as addresses_view
from nova.api.openstack.views import flavors as flavors_view
from nova.api.openstack.views import images as images_view
@@ -11,7 +29,7 @@ def get_view_builder(req):
A factory method that returns the correct builder based on the version of
the api requested.
'''
- version = req.environ['version']
+ version = common.get_api_version(req)
addresses_builder = addresses_view.get_view_builder(req)
if version == '1.1':
flavor_builder = flavors_view.get_view_builder(req)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 9c3b53ac7..a3968b57b 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -69,8 +69,8 @@ def fake_auth_init(self, application):
@webob.dec.wsgify
def fake_wsgi(self, req):
req.environ['nova.context'] = context.RequestContext(1, 1)
- if not req.environ.get('version'):
- req.environ['version'] = '1.0'
+ if not req.environ.get('api.version'):
+ req.environ['api.version'] = '1.0'
if req.body:
req.environ['inst_dict'] = json.loads(req.body)
return self.application
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 6b804d3b4..27d174fe9 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -183,7 +183,7 @@ class ServersTest(test.TestCase):
new_return_server = return_server_with_addresses(private, public)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
req = webob.Request.blank('/v1.1/servers/1')
- req.environ['version'] = '1.1'
+ req.environ['api.version'] = '1.1'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['server']['id'], '1')
@@ -363,7 +363,7 @@ class ServersTest(test.TestCase):
def test_get_all_server_details_v1_1(self):
req = webob.Request.blank('/v1.1/servers/detail')
- req.environ['version'] = '1.1'
+ req.environ['api.version'] = '1.1'
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
--
cgit
From c42d79b58eccaebab14274adf09128d890e920f7 Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Tue, 15 Mar 2011 20:37:37 -0400
Subject: adding imageRef and flavorRef attributes to servers serialization
metadata
---
nova/api/openstack/servers.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index de67cbc4a..dc62882eb 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -46,7 +46,8 @@ class Controller(wsgi.Controller):
'application/xml': {
"attributes": {
"server": ["id", "imageId", "name", "flavorId", "hostId",
- "status", "progress", "adminPass"]}}}
+ "status", "progress", "adminPass", "flavorRef",
+ "imageRef"]}}}
def __init__(self):
self.compute_api = compute.API()
--
cgit
From 5e45d0ba921566e98817cb9e62e383f84c30c5f6 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Tue, 15 Mar 2011 20:51:17 -0400
Subject: Limits controller and testing with XML and JSON serialization.
---
nova/api/openstack/limits.py | 42 ++-
nova/tests/api/openstack/test_limits.py | 524 ++++++++++++++++++++++++++
nova/tests/api/openstack/test_ratelimiting.py | 387 -------------------
nova/wsgi.py | 1 +
4 files changed, 556 insertions(+), 398 deletions(-)
create mode 100644 nova/tests/api/openstack/test_limits.py
delete mode 100644 nova/tests/api/openstack/test_ratelimiting.py
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index b1e633330..57e6bfcc2 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -50,12 +50,24 @@ class LimitsController(Controller):
Controller for accessing limits in the OpenStack API.
"""
+ _serialization_metadata = {
+ "application/xml": {
+ "attributes": {
+ "limit": ["verb", "URI", "regex", "value", "unit",
+ "resetTime", "remaining", "name"],
+ },
+ "plurals" : {
+ "rate" : "limit",
+ },
+ },
+ }
+
def index(self, req):
"""
Return all global and rate limit information.
"""
abs_limits = {}
- rate_limits = req.environ.get("nova.limits", {})
+ rate_limits = req.environ.get("nova.limits", [])
return {
"limits": {
@@ -92,6 +104,7 @@ class Limit(object):
self.regex = regex
self.value = int(value)
self.unit = unit
+ self.unit_string = self.display_unit().lower()
self.remaining = int(value)
if value <= 0:
@@ -101,8 +114,10 @@ class Limit(object):
self.next_request = None
self.water_level = 0
- self.capacity = float(self.unit)
+ self.capacity = self.unit
self.request_value = float(self.capacity) / float(self.value)
+ self.error_message = _("Only %(value)s %(verb)s request(s) can be "\
+ "made to %(uri)s every %(unit_string)s." % self.__dict__)
def __call__(self, verb, url):
"""
@@ -153,7 +168,7 @@ class Limit(object):
"""Return a useful representation of this class."""
return {
"verb": self.verb,
- "uri": self.uri,
+ "URI": self.uri,
"regex": self.regex,
"value": self.value,
"remaining": int(self.remaining),
@@ -204,13 +219,12 @@ class RateLimitingMiddleware(Middleware):
url = req.url
username = req.environ["nova.context"].user_id
- delay = self._limiter.check_for_delay(verb, url, username)
+ delay, error = self._limiter.check_for_delay(verb, url, username)
if delay:
msg = "This request was rate-limited."
- details = "Error details."
retry = time.time() + delay
- return faults.OverLimitFault(msg, details, retry)
+ return faults.OverLimitFault(msg, error, retry)
req.environ["nova.limits"] = self._limiter.get_limits(username)
@@ -240,13 +254,15 @@ class Limiter(object):
def check_for_delay(self, verb, url, username=None):
"""
Check the given verb/user/user triplet for limit.
+
+ @return: Tuple of delay (in seconds) and error message (or None, None)
"""
def _get_delay_list():
"""Yield limit delays."""
for limit in self.levels[username]:
delay = limit(verb, url)
if delay:
- yield delay
+ yield delay, limit.error_message
delays = list(_get_delay_list())
@@ -254,6 +270,8 @@ class Limiter(object):
delays.sort()
return delays[0]
+ return None, None
+
class WsgiLimiter(object):
"""
@@ -298,11 +316,11 @@ class WsgiLimiter(object):
verb = info.get("verb")
path = info.get("path")
- delay = self._limiter.check_for_delay(verb, path, username)
+ delay, error = self._limiter.check_for_delay(verb, path, username)
if delay:
headers = {"X-Wait-Seconds": "%.2f" % delay}
- return webob.exc.HTTPForbidden(headers=headers)
+ return webob.exc.HTTPForbidden(headers=headers, explanation=error)
else:
return webob.exc.HTTPNoContent()
@@ -333,7 +351,9 @@ class WsgiLimiterProxy(object):
resp = conn.getresponse()
+ print resp
+
if 200 >= resp.status < 300:
- return None
+ return None, None
- return resp.getheader("X-Wait-Seconds")
+ return resp.getheader("X-Wait-Seconds"), resp.read() or None
diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py
new file mode 100644
index 000000000..cf4389c1d
--- /dev/null
+++ b/nova/tests/api/openstack/test_limits.py
@@ -0,0 +1,524 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack LLC.
+# 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.
+
+"""
+Tests dealing with HTTP rate-limiting.
+"""
+
+import httplib
+import json
+import StringIO
+import stubout
+import time
+import webob
+
+from xml.dom.minidom import parseString
+
+from nova import test
+from nova.api.openstack import limits
+from nova.api.openstack.limits import Limit
+
+
+TEST_LIMITS = [
+ Limit("GET", "/delayed", "^/delayed", 1, limits.PER_MINUTE),
+ Limit("POST", "*", ".*", 7, limits.PER_MINUTE),
+ Limit("POST", "/servers", "^/servers", 3, limits.PER_MINUTE),
+ Limit("PUT", "*", "", 10, limits.PER_MINUTE),
+ Limit("PUT", "/servers", "^/servers", 5, limits.PER_MINUTE),
+]
+
+
+class LimitsControllerTest(test.TestCase):
+ """
+ Tests for `limits.LimitsController` class.
+ """
+
+ def setUp(self):
+ """Run before each test."""
+ test.TestCase.setUp(self)
+ self.time = 0.0
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ self.controller = limits.LimitsController()
+
+ def tearDown(self):
+ """Run after each test."""
+ self.stubs.UnsetAll()
+ test.TestCase.tearDown(self)
+
+ def _get_time(self):
+ """Return the "time" according to this test suite."""
+ return self.time
+
+ def _get_index_request(self, accept_header="application/json"):
+ """Helper to set routing arguments."""
+ request = webob.Request.blank("/")
+ request.accept = accept_header
+ request.environ["wsgiorg.routing_args"] = (None, {
+ "action": "index",
+ "controller": "",
+ })
+ return request
+
+ def _populate_limits(self, request):
+ """Put limit info into a request."""
+ limits = [
+ Limit("GET", "*", ".*", 10, 60).display(),
+ Limit("POST", "*", ".*", 5, 60 * 60).display(),
+ ]
+ request.environ["nova.limits"] = limits
+ return request
+
+ def test_empty_index_json(self):
+ """Test getting empty limit details in JSON."""
+ request = self._get_index_request()
+ response = request.get_response(self.controller)
+ expected = {
+ "limits": {
+ "rate": [],
+ "absolute": {},
+ },
+ }
+ body = json.loads(response.body)
+ self.assertEqual(expected, body)
+
+ def test_index_json(self):
+ """Test getting limit details in JSON."""
+ request = self._get_index_request()
+ request = self._populate_limits(request)
+ response = request.get_response(self.controller)
+ expected = {
+ "limits": {
+ "rate": [{
+ "regex": ".*",
+ "resetTime": 0,
+ "URI": "*",
+ "value": 10,
+ "verb": "GET",
+ "remaining": 10,
+ "unit": "MINUTE",
+ },
+ {
+ "regex": ".*",
+ "resetTime": 0,
+ "URI": "*",
+ "value": 5,
+ "verb": "POST",
+ "remaining": 5,
+ "unit": "HOUR",
+ }],
+ "absolute": {},
+ },
+ }
+ body = json.loads(response.body)
+ self.assertEqual(expected, body)
+
+ def test_empty_index_xml(self):
+ """Test getting limit details in XML."""
+ request = self._get_index_request("application/xml")
+ response = request.get_response(self.controller)
+
+ expected = ""
+ body = response.body.replace("\n","").replace(" ", "")
+
+ self.assertEqual(expected, body)
+
+ def test_index_xml(self):
+ """Test getting limit details in XML."""
+ request = self._get_index_request("application/xml")
+ request = self._populate_limits(request)
+ response = request.get_response(self.controller)
+
+ expected = parseString("""
+
+
+
+
+
+
+
+ """.replace(" ", ""))
+ body = parseString(response.body.replace(" ", ""))
+
+ self.assertEqual(expected.toxml(), body.toxml())
+
+
+class LimiterTest(test.TestCase):
+ """
+ Tests for the in-memory `limits.Limiter` class.
+ """
+
+ def setUp(self):
+ """Run before each test."""
+ test.TestCase.setUp(self)
+ self.time = 0.0
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ self.limiter = limits.Limiter(TEST_LIMITS)
+
+ def tearDown(self):
+ """Run after each test."""
+ self.stubs.UnsetAll()
+ test.TestCase.tearDown(self)
+
+ def _get_time(self):
+ """Return the "time" according to this test suite."""
+ return self.time
+
+ def _check(self, num, verb, url, username=None):
+ """Check and yield results from checks."""
+ for x in xrange(num):
+ yield self.limiter.check_for_delay(verb, url, username)[0]
+
+ def _check_sum(self, num, verb, url, username=None):
+ """Check and sum results from checks."""
+ results = self._check(num, verb, url, username)
+ return sum(filter(lambda x: x != None, results))
+
+ def test_no_delay_GET(self):
+ """
+ Simple test to ensure no delay on a single call for a limit verb we
+ didn"t set.
+ """
+ delay = self.limiter.check_for_delay("GET", "/anything")
+ self.assertEqual(delay, (None, None))
+
+ def test_no_delay_PUT(self):
+ """
+ Simple test to ensure no delay on a single call for a known limit.
+ """
+ delay = self.limiter.check_for_delay("PUT", "/anything")
+ self.assertEqual(delay, (None, None))
+
+ def test_delay_PUT(self):
+ """
+ Ensure the 11th PUT will result in a delay of 6.0 seconds until
+ the next request will be granced.
+ """
+ expected = [None] * 10 + [6.0]
+ results = list(self._check(11, "PUT", "/anything"))
+
+ self.assertEqual(expected, results)
+
+ def test_delay_POST(self):
+ """
+ Ensure the 8th POST will result in a delay of 6.0 seconds until
+ the next request will be granced.
+ """
+ expected = [None] * 7
+ results = list(self._check(7, "POST", "/anything"))
+ self.assertEqual(expected, results)
+
+ expected = 60.0 / 7.0
+ results = self._check_sum(1, "POST", "/anything")
+ self.failUnlessAlmostEqual(expected, results, 8)
+
+ def test_delay_GET(self):
+ """
+ Ensure the 11th GET will result in NO delay.
+ """
+ expected = [None] * 11
+ results = list(self._check(11, "GET", "/anything"))
+
+ self.assertEqual(expected, results)
+
+ def test_delay_PUT_servers(self):
+ """
+ Ensure PUT on /servers limits at 5 requests, and PUT elsewhere is still
+ OK after 5 requests...but then after 11 total requests, PUT limiting
+ kicks in.
+ """
+ # First 6 requests on PUT /servers
+ expected = [None] * 5 + [12.0]
+ results = list(self._check(6, "PUT", "/servers"))
+ self.assertEqual(expected, results)
+
+ # Next 5 request on PUT /anything
+ expected = [None] * 4 + [6.0]
+ results = list(self._check(5, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ def test_delay_PUT_wait(self):
+ """
+ Ensure after hitting the limit and then waiting for the correct
+ amount of time, the limit will be lifted.
+ """
+ expected = [None] * 10 + [6.0]
+ results = list(self._check(11, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ # Advance time
+ self.time += 6.0
+
+ expected = [None, 6.0]
+ results = list(self._check(2, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ def test_multiple_delays(self):
+ """
+ Ensure multiple requests still get a delay.
+ """
+ expected = [None] * 10 + [6.0] * 10
+ results = list(self._check(20, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ self.time += 1.0
+
+ expected = [5.0] * 10
+ results = list(self._check(10, "PUT", "/anything"))
+ self.assertEqual(expected, results)
+
+ def test_multiple_users(self):
+ """
+ Tests involving multiple users.
+ """
+ # User1
+ expected = [None] * 10 + [6.0] * 10
+ results = list(self._check(20, "PUT", "/anything", "user1"))
+ self.assertEqual(expected, results)
+
+ # User2
+ expected = [None] * 10 + [6.0] * 5
+ results = list(self._check(15, "PUT", "/anything", "user2"))
+ self.assertEqual(expected, results)
+
+ self.time += 1.0
+
+ # User1 again
+ expected = [5.0] * 10
+ results = list(self._check(10, "PUT", "/anything", "user1"))
+ self.assertEqual(expected, results)
+
+ self.time += 1.0
+
+ # User1 again
+ expected = [4.0] * 5
+ results = list(self._check(5, "PUT", "/anything", "user2"))
+ self.assertEqual(expected, results)
+
+
+class WsgiLimiterTest(test.TestCase):
+ """
+ Tests for `limits.WsgiLimiter` class.
+ """
+
+ def setUp(self):
+ """Run before each test."""
+ test.TestCase.setUp(self)
+ self.time = 0.0
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ self.app = limits.WsgiLimiter(TEST_LIMITS)
+
+ def tearDown(self):
+ """Run after each test."""
+ self.stubs.UnsetAll()
+ test.TestCase.tearDown(self)
+
+ def _get_time(self):
+ """Return the "time" according to this test suite."""
+ return self.time
+
+ def _request_data(self, verb, path):
+ """Get data decribing a limit request verb/path."""
+ return json.dumps({"verb": verb, "path": path})
+
+ def _request(self, verb, url, username=None):
+ """Make sure that POSTing to the given url causes the given username
+ to perform the given action. Make the internal rate limiter return
+ delay and make sure that the WSGI app returns the correct response.
+ """
+ if username:
+ request = webob.Request.blank("/%s" % username)
+ else:
+ request = webob.Request.blank("/")
+
+ request.method = "POST"
+ request.body = self._request_data(verb, url)
+ response = request.get_response(self.app)
+
+ if "X-Wait-Seconds" in response.headers:
+ self.assertEqual(response.status_int, 403)
+ return response.headers["X-Wait-Seconds"]
+
+ self.assertEqual(response.status_int, 204)
+
+ def test_invalid_methods(self):
+ """Only POSTs should work."""
+ requests = []
+ for method in ["GET", "PUT", "DELETE", "HEAD", "OPTIONS"]:
+ request = webob.Request.blank("/")
+ request.body = self._request_data("GET", "/something")
+ response = request.get_response(self.app)
+ self.assertEqual(response.status_int, 405)
+
+ def test_good_url(self):
+ delay = self._request("GET", "/something")
+ self.assertEqual(delay, None)
+
+ def test_escaping(self):
+ delay = self._request("GET", "/something/jump%20up")
+ self.assertEqual(delay, None)
+
+ def test_response_to_delays(self):
+ delay = self._request("GET", "/delayed")
+ self.assertEqual(delay, None)
+
+ delay = self._request("GET", "/delayed")
+ self.assertEqual(delay, '60.00')
+
+ def test_response_to_delays_usernames(self):
+ delay = self._request("GET", "/delayed", "user1")
+ self.assertEqual(delay, None)
+
+ delay = self._request("GET", "/delayed", "user2")
+ self.assertEqual(delay, None)
+
+ delay = self._request("GET", "/delayed", "user1")
+ self.assertEqual(delay, '60.00')
+
+ delay = self._request("GET", "/delayed", "user2")
+ self.assertEqual(delay, '60.00')
+
+
+class FakeHttplibSocket(object):
+ """
+ Fake `httplib.HTTPResponse` replacement.
+ """
+
+ def __init__(self, response_string):
+ """Initialize new `FakeHttplibSocket`."""
+ self._buffer = StringIO.StringIO(response_string)
+
+ def makefile(self, _mode, _other):
+ """Returns the socket's internal buffer."""
+ return self._buffer
+
+
+class FakeHttplibConnection(object):
+ """
+ Fake `httplib.HTTPConnection`.
+ """
+
+ def __init__(self, app, host):
+ """
+ Initialize `FakeHttplibConnection`.
+ """
+ self.app = app
+ self.host = host
+
+ def request(self, method, path, body="", headers={}):
+ """
+ Requests made via this connection actually get translated and routed
+ into our WSGI app, we then wait for the response and turn it back into
+ an `httplib.HTTPResponse`.
+ """
+ req = webob.Request.blank(path)
+ req.method = method
+ req.headers = headers
+ req.host = self.host
+ req.body = body
+
+ resp = str(req.get_response(self.app))
+ resp = "HTTP/1.0 %s" % resp
+ sock = FakeHttplibSocket(resp)
+ self.http_response = httplib.HTTPResponse(sock)
+ self.http_response.begin()
+
+ def getresponse(self):
+ """Return our generated response from the request."""
+ return self.http_response
+
+
+def wire_HTTPConnection_to_WSGI(host, app):
+ """Monkeypatches HTTPConnection so that if you try to connect to host, you
+ are instead routed straight to the given WSGI app.
+
+ After calling this method, when any code calls
+
+ httplib.HTTPConnection(host)
+
+ the connection object will be a fake. Its requests will be sent directly
+ to the given WSGI app rather than through a socket.
+
+ Code connecting to hosts other than host will not be affected.
+
+ This method may be called multiple times to map different hosts to
+ different apps.
+ """
+ class HTTPConnectionDecorator(object):
+ """Wraps the real HTTPConnection class so that when you instantiate
+ the class you might instead get a fake instance."""
+
+ def __init__(self, wrapped):
+ self.wrapped = wrapped
+
+ def __call__(self, connection_host, *args, **kwargs):
+ if connection_host == host:
+ return FakeHttplibConnection(app, host)
+ else:
+ return self.wrapped(connection_host, *args, **kwargs)
+
+ httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection)
+
+
+class WsgiLimiterProxyTest(test.TestCase):
+ """
+ Tests for the `limits.WsgiLimiterProxy` class.
+ """
+
+ def setUp(self):
+ """
+ Do some nifty HTTP/WSGI magic which allows for WSGI to be called
+ directly by something like the `httplib` library.
+ """
+ test.TestCase.setUp(self)
+ self.time = 0.0
+ self.stubs = stubout.StubOutForTesting()
+ self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ self.app = limits.WsgiLimiter(TEST_LIMITS)
+ wire_HTTPConnection_to_WSGI("169.254.0.1:80", self.app)
+ self.proxy = limits.WsgiLimiterProxy("169.254.0.1:80")
+
+ def tearDown(self):
+ """Run after each test."""
+ self.stubs.UnsetAll()
+ test.TestCase.tearDown(self)
+
+ def _get_time(self):
+ """Return the "time" according to this test suite."""
+ return self.time
+
+ def test_200(self):
+ """Successful request test."""
+ delay = self.proxy.check_for_delay("GET", "/anything")
+ self.assertEqual(delay, (None, None))
+
+ def test_403(self):
+ """Forbidden request test."""
+ delay = self.proxy.check_for_delay("GET", "/delayed")
+ self.assertEqual(delay, (None, None))
+
+ delay, error = self.proxy.check_for_delay("GET", "/delayed")
+ error = error.strip()
+
+ expected = ("60.00", "403 Forbidden\n\nOnly 1 GET request(s) can be "\
+ "made to /delayed every minute.")
+
+ self.assertEqual((delay, error), expected)
diff --git a/nova/tests/api/openstack/test_ratelimiting.py b/nova/tests/api/openstack/test_ratelimiting.py
deleted file mode 100644
index a706364b4..000000000
--- a/nova/tests/api/openstack/test_ratelimiting.py
+++ /dev/null
@@ -1,387 +0,0 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2010 OpenStack LLC.
-# 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.
-
-"""
-Tests dealing with HTTP rate-limiting.
-"""
-
-import httplib
-import json
-import StringIO
-import stubout
-import time
-import webob
-
-from nova import test
-from nova.api.openstack import limits
-from nova.api.openstack.limits import Limit
-
-
-TEST_LIMITS = [
- Limit("GET", "/delayed", "^/delayed", 1, limits.PER_MINUTE),
- Limit("POST", "*", ".*", 7, limits.PER_MINUTE),
- Limit("POST", "/servers", "^/servers", 3, limits.PER_MINUTE),
- Limit("PUT", "*", "", 10, limits.PER_MINUTE),
- Limit("PUT", "/servers", "^/servers", 5, limits.PER_MINUTE),
-]
-
-
-class LimiterTest(test.TestCase):
- """
- Tests for the in-memory `limits.Limiter` class.
- """
-
- def setUp(self):
- """Run before each test."""
- test.TestCase.setUp(self)
- self.time = 0.0
- self.stubs = stubout.StubOutForTesting()
- self.stubs.Set(limits.Limit, "_get_time", self._get_time)
- self.limiter = limits.Limiter(TEST_LIMITS)
-
- def tearDown(self):
- """Run after each test."""
- self.stubs.UnsetAll()
-
- def _get_time(self):
- """Return the "time" according to this test suite."""
- return self.time
-
- def _check(self, num, verb, url, username=None):
- """Check and yield results from checks."""
- for x in xrange(num):
- yield self.limiter.check_for_delay(verb, url, username)
-
- def _check_sum(self, num, verb, url, username=None):
- """Check and sum results from checks."""
- results = self._check(num, verb, url, username)
- return sum(filter(lambda x: x != None, results))
-
- def test_no_delay_GET(self):
- """
- Simple test to ensure no delay on a single call for a limit verb we
- didn"t set.
- """
- delay = self.limiter.check_for_delay("GET", "/anything")
- self.assertEqual(delay, None)
-
- def test_no_delay_PUT(self):
- """
- Simple test to ensure no delay on a single call for a known limit.
- """
- delay = self.limiter.check_for_delay("PUT", "/anything")
- self.assertEqual(delay, None)
-
- def test_delay_PUT(self):
- """
- Ensure the 11th PUT will result in a delay of 6.0 seconds until
- the next request will be granced.
- """
- expected = [None] * 10 + [6.0]
- results = list(self._check(11, "PUT", "/anything"))
-
- self.assertEqual(expected, results)
-
- def test_delay_POST(self):
- """
- Ensure the 8th POST will result in a delay of 6.0 seconds until
- the next request will be granced.
- """
- expected = [None] * 7
- results = list(self._check(7, "POST", "/anything"))
- self.assertEqual(expected, results)
-
- expected = 60.0 / 7.0
- results = self._check_sum(1, "POST", "/anything")
- self.failUnlessAlmostEqual(expected, results, 8)
-
- def test_delay_GET(self):
- """
- Ensure the 11th GET will result in NO delay.
- """
- expected = [None] * 11
- results = list(self._check(11, "GET", "/anything"))
-
- self.assertEqual(expected, results)
-
- def test_delay_PUT_servers(self):
- """
- Ensure PUT on /servers limits at 5 requests, and PUT elsewhere is still
- OK after 5 requests...but then after 11 total requests, PUT limiting
- kicks in.
- """
- # First 6 requests on PUT /servers
- expected = [None] * 5 + [12.0]
- results = list(self._check(6, "PUT", "/servers"))
- self.assertEqual(expected, results)
-
- # Next 5 request on PUT /anything
- expected = [None] * 4 + [6.0]
- results = list(self._check(5, "PUT", "/anything"))
- self.assertEqual(expected, results)
-
- def test_delay_PUT_wait(self):
- """
- Ensure after hitting the limit and then waiting for the correct
- amount of time, the limit will be lifted.
- """
- expected = [None] * 10 + [6.0]
- results = list(self._check(11, "PUT", "/anything"))
- self.assertEqual(expected, results)
-
- # Advance time
- self.time += 6.0
-
- expected = [None, 6.0]
- results = list(self._check(2, "PUT", "/anything"))
- self.assertEqual(expected, results)
-
- def test_multiple_delays(self):
- """
- Ensure multiple requests still get a delay.
- """
- expected = [None] * 10 + [6.0] * 10
- results = list(self._check(20, "PUT", "/anything"))
- self.assertEqual(expected, results)
-
- self.time += 1.0
-
- expected = [5.0] * 10
- results = list(self._check(10, "PUT", "/anything"))
- self.assertEqual(expected, results)
-
- def test_multiple_users(self):
- """
- Tests involving multiple users.
- """
- # User1
- expected = [None] * 10 + [6.0] * 10
- results = list(self._check(20, "PUT", "/anything", "user1"))
- self.assertEqual(expected, results)
-
- # User2
- expected = [None] * 10 + [6.0] * 5
- results = list(self._check(15, "PUT", "/anything", "user2"))
- self.assertEqual(expected, results)
-
- self.time += 1.0
-
- # User1 again
- expected = [5.0] * 10
- results = list(self._check(10, "PUT", "/anything", "user1"))
- self.assertEqual(expected, results)
-
- self.time += 1.0
-
- # User1 again
- expected = [4.0] * 5
- results = list(self._check(5, "PUT", "/anything", "user2"))
- self.assertEqual(expected, results)
-
-
-class WsgiLimiterTest(test.TestCase):
- """
- Tests for `limits.WsgiLimiter` class.
- """
-
- def setUp(self):
- """Run before each test."""
- test.TestCase.setUp(self)
- self.time = 0.0
- self.app = limits.WsgiLimiter(TEST_LIMITS)
- self.app._limiter._get_time = self._get_time
-
- def _get_time(self):
- """Return the "time" according to this test suite."""
- return self.time
-
- def _request_data(self, verb, path):
- """Get data decribing a limit request verb/path."""
- return json.dumps({"verb": verb, "path": path})
-
- def _request(self, verb, url, username=None):
- """Make sure that POSTing to the given url causes the given username
- to perform the given action. Make the internal rate limiter return
- delay and make sure that the WSGI app returns the correct response.
- """
- if username:
- request = webob.Request.blank("/%s" % username)
- else:
- request = webob.Request.blank("/")
-
- request.method = "POST"
- request.body = self._request_data(verb, url)
- response = request.get_response(self.app)
-
- if "X-Wait-Seconds" in response.headers:
- self.assertEqual(response.status_int, 403)
- return response.headers["X-Wait-Seconds"]
-
- self.assertEqual(response.status_int, 204)
-
- def test_invalid_methods(self):
- """Only POSTs should work."""
- requests = []
- for method in ["GET", "PUT", "DELETE", "HEAD", "OPTIONS"]:
- request = webob.Request.blank("/")
- request.body = self._request_data("GET", "/something")
- response = request.get_response(self.app)
- self.assertEqual(response.status_int, 405)
-
- def test_good_url(self):
- delay = self._request("GET", "/something")
- self.assertEqual(delay, None)
-
- def test_escaping(self):
- delay = self._request("GET", "/something/jump%20up")
- self.assertEqual(delay, None)
-
- def test_response_to_delays(self):
- delay = self._request("GET", "/delayed")
- self.assertEqual(delay, None)
-
- delay = self._request("GET", "/delayed")
- self.assertEqual(delay, '60.00')
-
- def test_response_to_delays_usernames(self):
- delay = self._request("GET", "/delayed", "user1")
- self.assertEqual(delay, None)
-
- delay = self._request("GET", "/delayed", "user2")
- self.assertEqual(delay, None)
-
- delay = self._request("GET", "/delayed", "user1")
- self.assertEqual(delay, '60.00')
-
- delay = self._request("GET", "/delayed", "user2")
- self.assertEqual(delay, '60.00')
-
-
-class FakeHttplibSocket(object):
- """
- Fake `httplib.HTTPResponse` replacement.
- """
-
- def __init__(self, response_string):
- """Initialize new `FakeHttplibSocket`."""
- self._buffer = StringIO.StringIO(response_string)
-
- def makefile(self, _mode, _other):
- """Returns the socket's internal buffer."""
- return self._buffer
-
-
-class FakeHttplibConnection(object):
- """
- Fake `httplib.HTTPConnection`.
- """
-
- def __init__(self, app, host):
- """
- Initialize `FakeHttplibConnection`.
- """
- self.app = app
- self.host = host
-
- def request(self, method, path, body="", headers={}):
- """
- Requests made via this connection actually get translated and routed
- into our WSGI app, we then wait for the response and turn it back into
- an `httplib.HTTPResponse`.
- """
- req = webob.Request.blank(path)
- req.method = method
- req.headers = headers
- req.host = self.host
- req.body = body
-
- resp = str(req.get_response(self.app))
- resp = "HTTP/1.0 %s" % resp
- sock = FakeHttplibSocket(resp)
- self.http_response = httplib.HTTPResponse(sock)
- self.http_response.begin()
-
- def getresponse(self):
- """Return our generated response from the request."""
- return self.http_response
-
-
-def wire_HTTPConnection_to_WSGI(host, app):
- """Monkeypatches HTTPConnection so that if you try to connect to host, you
- are instead routed straight to the given WSGI app.
-
- After calling this method, when any code calls
-
- httplib.HTTPConnection(host)
-
- the connection object will be a fake. Its requests will be sent directly
- to the given WSGI app rather than through a socket.
-
- Code connecting to hosts other than host will not be affected.
-
- This method may be called multiple times to map different hosts to
- different apps.
- """
- class HTTPConnectionDecorator(object):
- """Wraps the real HTTPConnection class so that when you instantiate
- the class you might instead get a fake instance."""
-
- def __init__(self, wrapped):
- self.wrapped = wrapped
-
- def __call__(self, connection_host, *args, **kwargs):
- if connection_host == host:
- return FakeHttplibConnection(app, host)
- else:
- return self.wrapped(connection_host, *args, **kwargs)
-
- httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection)
-
-
-class WsgiLimiterProxyTest(test.TestCase):
- """
- Tests for the `limits.WsgiLimiterProxy` class.
- """
-
- def setUp(self):
- """
- Do some nifty HTTP/WSGI magic which allows for WSGI to be called
- directly by something like the `httplib` library.
- """
- test.TestCase.setUp(self)
- self.time = 0.0
- self.app = limits.WsgiLimiter(TEST_LIMITS)
- self.app._limiter._get_time = self._get_time
- wire_HTTPConnection_to_WSGI("169.254.0.1:80", self.app)
- self.proxy = limits.WsgiLimiterProxy("169.254.0.1:80")
-
- def _get_time(self):
- """Return the "time" according to this test suite."""
- return self.time
-
- def test_200(self):
- """Successful request test."""
- delay = self.proxy.check_for_delay("GET", "/anything")
- self.assertEqual(delay, None)
-
- def test_403(self):
- """Forbidden request test."""
- delay = self.proxy.check_for_delay("GET", "/delayed")
- self.assertEqual(delay, None)
-
- delay = self.proxy.check_for_delay("GET", "/delayed")
- self.assertEqual(delay, '60.00')
diff --git a/nova/wsgi.py b/nova/wsgi.py
index ba0819466..21aabd556 100644
--- a/nova/wsgi.py
+++ b/nova/wsgi.py
@@ -482,6 +482,7 @@ class Serializer(object):
def _to_xml_node(self, doc, metadata, nodename, data):
"""Recursive method to convert data members to XML nodes."""
+ print "to_xml_node(%s, %s, %s, %s)" % (doc, metadata, nodename, data)
result = doc.createElement(nodename)
if type(data) is list:
singular = metadata.get('plurals', {}).get(nodename, None)
--
cgit
From 5ba3e21875d3cf3b71082477311902891706eee4 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Tue, 15 Mar 2011 21:09:26 -0400
Subject: Removed VIM specific stuff and changed copyright from 2010 to 2011.
---
nova/api/openstack/limits.py | 4 +---
nova/tests/api/openstack/test_limits.py | 4 +---
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index 57e6bfcc2..3ecd46377 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -1,6 +1,4 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2010 OpenStack LLC.
+# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py
index cf4389c1d..40178e671 100644
--- a/nova/tests/api/openstack/test_limits.py
+++ b/nova/tests/api/openstack/test_limits.py
@@ -1,6 +1,4 @@
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2010 OpenStack LLC.
+# Copyright 2011 OpenStack LLC.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
--
cgit
From 6911123fda88c9793a70ea4b03d0352c9c38f938 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Tue, 15 Mar 2011 21:26:45 -0400
Subject: Adding newlines for pep8.
---
nova/api/openstack/common.py | 1 +
nova/api/openstack/views/flavors.py | 1 +
nova/api/openstack/views/images.py | 1 +
3 files changed, 3 insertions(+)
diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py
index d94969ff5..d6679de01 100644
--- a/nova/api/openstack/common.py
+++ b/nova/api/openstack/common.py
@@ -75,5 +75,6 @@ def get_image_id_from_image_hash(image_service, context, image_hash):
return image_id
raise exception.NotFound(image_hash)
+
def get_api_version(req):
return req.environ.get('api.version')
diff --git a/nova/api/openstack/views/flavors.py b/nova/api/openstack/views/flavors.py
index aa3c2aeb2..dd2e75a7a 100644
--- a/nova/api/openstack/views/flavors.py
+++ b/nova/api/openstack/views/flavors.py
@@ -17,6 +17,7 @@
from nova.api.openstack import common
+
def get_view_builder(req):
'''
A factory method that returns the correct builder based on the version of
diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py
index 930b464b0..2369a8f9d 100644
--- a/nova/api/openstack/views/images.py
+++ b/nova/api/openstack/views/images.py
@@ -17,6 +17,7 @@
from nova.api.openstack import common
+
def get_view_builder(req):
'''
A factory method that returns the correct builder based on the version of
--
cgit
From 60c7ce60826becb1ebe7f75a0a0d28b2893d70c0 Mon Sep 17 00:00:00 2001
From: Ken Pepple
Date: Tue, 15 Mar 2011 18:54:51 -0700
Subject: revised per code review
---
nova/image/local.py | 11 +++++++----
nova/tests/api/openstack/test_images.py | 9 +++++++--
2 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/nova/image/local.py b/nova/image/local.py
index ef92a35b5..c304a2212 100644
--- a/nova/image/local.py
+++ b/nova/image/local.py
@@ -51,12 +51,15 @@ class LocalImageService(service.BaseImageService):
def _ids(self):
"""The list of all image ids."""
images = []
- for i in os.listdir(self._path):
+ for image_dir in os.listdir(self._path):
try:
- images.append(int(i, 16))
+ images.append(int(image_dir, 16))
+ except ValueError:
+ LOG.error(
+ _("%s is not in correct directory naming format"\
+ % image_dir))
except:
- LOG.debug(
- _("%s is not in correct directory naming format" % i))
+ raise
return images
def index(self, context):
diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py
index 2c4918117..a674ccefe 100644
--- a/nova/tests/api/openstack/test_images.py
+++ b/nova/tests/api/openstack/test_images.py
@@ -22,6 +22,7 @@ and as a WSGI layer
import json
import datetime
+import os
import shutil
import tempfile
@@ -155,8 +156,12 @@ class LocalImageServiceTest(test.TestCase,
# create some old-style image directories (starting with 'ami-')
for x in [1, 2, 3]:
tempfile.mkstemp(prefix='ami-', dir=self.tempdir)
- found_images = self.service._ids()
- self.assertEqual(True, isinstance(found_images, list))
+ # create some valid image directories names
+ for x in ["1485baed", "1a60f0ee", "3123a73d"]:
+ os.makedirs(os.path.join(self.tempdir, x))
+ found_image_ids = self.service._ids()
+ self.assertEqual(True, isinstance(found_image_ids, list))
+ self.assertEqual(3, len(found_image_ids), len(found_image_ids))
class GlanceImageServiceTest(test.TestCase,
--
cgit
From be9a218e2e4b01fe19722fb0073731d8ae6a7eea Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Tue, 15 Mar 2011 23:13:05 -0400
Subject: Added tests back for RateLimitingMiddleware which now throw correctly
serialized errors with correct error codes.
Removed some error printing, and simplified some other parts of the code with
suggestions from teammates.
---
nova/api/openstack/faults.py | 22 +++-
nova/api/openstack/limits.py | 25 ++---
nova/tests/api/openstack/test_limits.py | 172 ++++++++++++++++++++++----------
nova/wsgi.py | 1 -
4 files changed, 148 insertions(+), 72 deletions(-)
diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py
index 6ed9322de..d05c61fc7 100644
--- a/nova/api/openstack/faults.py
+++ b/nova/api/openstack/faults.py
@@ -68,17 +68,31 @@ class OverLimitFault(webob.exc.HTTPException):
Rate-limited request response.
"""
- wrapped_exc = webob.exc.HTTPForbidden()
+ _serialization_metadata = {
+ "application/xml": {
+ "attributes": {
+ "overLimitFault": "code"
+ }
+ }
+ }
def __init__(self, message, details, retry_time):
"""
Initialize new `OverLimitFault` with relevant information.
"""
- self.message = message
- self.details = details
- self.retry_time = retry_time
+ self.wrapped_exc = webob.exc.HTTPForbidden()
+ self.content = {
+ "overLimitFault": {
+ "code": self.wrapped_exc.status_int,
+ "message": message,
+ "details": details,
+ },
+ }
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, request):
"""Currently just return the wrapped exception."""
+ serializer = wsgi.Serializer(self._serialization_metadata)
+ content_type = request.best_match_content_type()
+ self.wrapped_exc.body = serializer.serialize(self.content, content_type)
return self.wrapped_exc
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index 3ecd46377..c4e04e9d9 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -54,8 +54,8 @@ class LimitsController(Controller):
"limit": ["verb", "URI", "regex", "value", "unit",
"resetTime", "remaining", "name"],
},
- "plurals" : {
- "rate" : "limit",
+ "plurals": {
+ "rate": "limit",
},
},
}
@@ -215,7 +215,12 @@ class RateLimitingMiddleware(Middleware):
"""
verb = req.method
url = req.url
- username = req.environ["nova.context"].user_id
+ context = req.environ.get("nova.context")
+
+ if context:
+ username = context.user_id
+ else:
+ username = None
delay, error = self._limiter.check_for_delay(verb, url, username)
@@ -255,14 +260,12 @@ class Limiter(object):
@return: Tuple of delay (in seconds) and error message (or None, None)
"""
- def _get_delay_list():
- """Yield limit delays."""
- for limit in self.levels[username]:
- delay = limit(verb, url)
- if delay:
- yield delay, limit.error_message
+ delays = []
- delays = list(_get_delay_list())
+ for limit in self.levels[username]:
+ delay = limit(verb, url)
+ if delay:
+ delays.append((delay, limit.error_message))
if delays:
delays.sort()
@@ -349,8 +352,6 @@ class WsgiLimiterProxy(object):
resp = conn.getresponse()
- print resp
-
if 200 >= resp.status < 300:
return None, None
diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py
index 40178e671..d1db93a59 100644
--- a/nova/tests/api/openstack/test_limits.py
+++ b/nova/tests/api/openstack/test_limits.py
@@ -22,11 +22,11 @@ import json
import StringIO
import stubout
import time
+import unittest
import webob
from xml.dom.minidom import parseString
-from nova import test
from nova.api.openstack import limits
from nova.api.openstack.limits import Limit
@@ -40,28 +40,34 @@ TEST_LIMITS = [
]
-class LimitsControllerTest(test.TestCase):
- """
- Tests for `limits.LimitsController` class.
- """
+class BaseLimitTestSuite(unittest.TestCase):
+ """Base test suite which provides relevant stubs and time abstraction."""
def setUp(self):
"""Run before each test."""
- test.TestCase.setUp(self)
self.time = 0.0
self.stubs = stubout.StubOutForTesting()
self.stubs.Set(limits.Limit, "_get_time", self._get_time)
- self.controller = limits.LimitsController()
def tearDown(self):
"""Run after each test."""
self.stubs.UnsetAll()
- test.TestCase.tearDown(self)
def _get_time(self):
"""Return the "time" according to this test suite."""
return self.time
+
+class LimitsControllerTest(BaseLimitTestSuite):
+ """
+ Tests for `limits.LimitsController` class.
+ """
+
+ def setUp(self):
+ """Run before each test."""
+ BaseLimitTestSuite.setUp(self)
+ self.controller = limits.LimitsController()
+
def _get_index_request(self, accept_header="application/json"):
"""Helper to set routing arguments."""
request = webob.Request.blank("/")
@@ -74,11 +80,11 @@ class LimitsControllerTest(test.TestCase):
def _populate_limits(self, request):
"""Put limit info into a request."""
- limits = [
+ _limits = [
Limit("GET", "*", ".*", 10, 60).display(),
Limit("POST", "*", ".*", 5, 60 * 60).display(),
]
- request.environ["nova.limits"] = limits
+ request.environ["nova.limits"] = _limits
return request
def test_empty_index_json(self):
@@ -131,7 +137,7 @@ class LimitsControllerTest(test.TestCase):
response = request.get_response(self.controller)
expected = ""
- body = response.body.replace("\n","").replace(" ", "")
+ body = response.body.replace("\n", "").replace(" ", "")
self.assertEqual(expected, body)
@@ -144,7 +150,7 @@ class LimitsControllerTest(test.TestCase):
expected = parseString("""
-
@@ -157,28 +163,108 @@ class LimitsControllerTest(test.TestCase):
self.assertEqual(expected.toxml(), body.toxml())
-class LimiterTest(test.TestCase):
+class LimitMiddlewareTest(BaseLimitTestSuite):
+ """
+ Tests for the `limits.RateLimitingMiddleware` class.
+ """
+
+ @webob.dec.wsgify
+ def _empty_app(self, request):
+ """Do-nothing WSGI app."""
+ pass
+
+ def setUp(self):
+ """Prepare middleware for use through fake WSGI app."""
+ BaseLimitTestSuite.setUp(self)
+ _limits = [
+ Limit("GET", "*", ".*", 1, 60),
+ ]
+ self.app = limits.RateLimitingMiddleware(self._empty_app, _limits)
+
+ def test_good_request(self):
+ """Test successful GET request through middleware."""
+ request = webob.Request.blank("/")
+ response = request.get_response(self.app)
+ self.assertEqual(200, response.status_int)
+
+ def test_limited_request_json(self):
+ """Test a rate-limited (403) GET request through middleware."""
+ request = webob.Request.blank("/")
+ response = request.get_response(self.app)
+ self.assertEqual(200, response.status_int)
+
+ request = webob.Request.blank("/")
+ response = request.get_response(self.app)
+ self.assertEqual(response.status_int, 403)
+
+ body = json.loads(response.body)
+ expected = "Only 1 GET request(s) can be made to * every minute."
+ value = body["overLimitFault"]["details"].strip()
+ self.assertEqual(value, expected)
+
+ def test_limited_request_xml(self):
+ """Test a rate-limited (403) response as XML"""
+ request = webob.Request.blank("/")
+ response = request.get_response(self.app)
+ self.assertEqual(200, response.status_int)
+
+ request = webob.Request.blank("/")
+ request.accept = "application/xml"
+ response = request.get_response(self.app)
+ self.assertEqual(response.status_int, 403)
+
+ root = parseString(response.body).childNodes[0]
+ expected = "Only 1 GET request(s) can be made to * every minute."
+
+ details = root.getElementsByTagName("details")
+ self.assertEqual(details.length, 1)
+
+ value = details.item(0).firstChild.data.strip()
+ self.assertEqual(value, expected)
+
+
+class LimitTest(BaseLimitTestSuite):
+ """
+ Tests for the `limits.Limit` class.
+ """
+
+ def test_GET_no_delay(self):
+ """Test a limit handles 1 GET per second."""
+ limit = Limit("GET", "*", ".*", 1, 1)
+ delay = limit("GET", "/anything")
+ self.assertEqual(None, delay)
+ self.assertEqual(0, limit.next_request)
+ self.assertEqual(0, limit.last_request)
+
+ def test_GET_delay(self):
+ """Test two calls to 1 GET per second limit."""
+ limit = Limit("GET", "*", ".*", 1, 1)
+ delay = limit("GET", "/anything")
+ self.assertEqual(None, delay)
+
+ delay = limit("GET", "/anything")
+ self.assertEqual(1, delay)
+ self.assertEqual(1, limit.next_request)
+ self.assertEqual(0, limit.last_request)
+
+ self.time += 4
+
+ delay = limit("GET", "/anything")
+ self.assertEqual(None, delay)
+ self.assertEqual(4, limit.next_request)
+ self.assertEqual(4, limit.last_request)
+
+
+class LimiterTest(BaseLimitTestSuite):
"""
Tests for the in-memory `limits.Limiter` class.
"""
def setUp(self):
"""Run before each test."""
- test.TestCase.setUp(self)
- self.time = 0.0
- self.stubs = stubout.StubOutForTesting()
- self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ BaseLimitTestSuite.setUp(self)
self.limiter = limits.Limiter(TEST_LIMITS)
- def tearDown(self):
- """Run after each test."""
- self.stubs.UnsetAll()
- test.TestCase.tearDown(self)
-
- def _get_time(self):
- """Return the "time" according to this test suite."""
- return self.time
-
def _check(self, num, verb, url, username=None):
"""Check and yield results from checks."""
for x in xrange(num):
@@ -311,28 +397,16 @@ class LimiterTest(test.TestCase):
self.assertEqual(expected, results)
-class WsgiLimiterTest(test.TestCase):
+class WsgiLimiterTest(BaseLimitTestSuite):
"""
Tests for `limits.WsgiLimiter` class.
"""
def setUp(self):
"""Run before each test."""
- test.TestCase.setUp(self)
- self.time = 0.0
- self.stubs = stubout.StubOutForTesting()
- self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ BaseLimitTestSuite.setUp(self)
self.app = limits.WsgiLimiter(TEST_LIMITS)
- def tearDown(self):
- """Run after each test."""
- self.stubs.UnsetAll()
- test.TestCase.tearDown(self)
-
- def _get_time(self):
- """Return the "time" according to this test suite."""
- return self.time
-
def _request_data(self, verb, path):
"""Get data decribing a limit request verb/path."""
return json.dumps({"verb": verb, "path": path})
@@ -476,7 +550,7 @@ def wire_HTTPConnection_to_WSGI(host, app):
httplib.HTTPConnection = HTTPConnectionDecorator(httplib.HTTPConnection)
-class WsgiLimiterProxyTest(test.TestCase):
+class WsgiLimiterProxyTest(BaseLimitTestSuite):
"""
Tests for the `limits.WsgiLimiterProxy` class.
"""
@@ -486,23 +560,11 @@ class WsgiLimiterProxyTest(test.TestCase):
Do some nifty HTTP/WSGI magic which allows for WSGI to be called
directly by something like the `httplib` library.
"""
- test.TestCase.setUp(self)
- self.time = 0.0
- self.stubs = stubout.StubOutForTesting()
- self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ BaseLimitTestSuite.setUp(self)
self.app = limits.WsgiLimiter(TEST_LIMITS)
wire_HTTPConnection_to_WSGI("169.254.0.1:80", self.app)
self.proxy = limits.WsgiLimiterProxy("169.254.0.1:80")
- def tearDown(self):
- """Run after each test."""
- self.stubs.UnsetAll()
- test.TestCase.tearDown(self)
-
- def _get_time(self):
- """Return the "time" according to this test suite."""
- return self.time
-
def test_200(self):
"""Successful request test."""
delay = self.proxy.check_for_delay("GET", "/anything")
@@ -519,4 +581,4 @@ class WsgiLimiterProxyTest(test.TestCase):
expected = ("60.00", "403 Forbidden\n\nOnly 1 GET request(s) can be "\
"made to /delayed every minute.")
- self.assertEqual((delay, error), expected)
+ self.assertEqual((delay, error), expected)
diff --git a/nova/wsgi.py b/nova/wsgi.py
index 21aabd556..ba0819466 100644
--- a/nova/wsgi.py
+++ b/nova/wsgi.py
@@ -482,7 +482,6 @@ class Serializer(object):
def _to_xml_node(self, doc, metadata, nodename, data):
"""Recursive method to convert data members to XML nodes."""
- print "to_xml_node(%s, %s, %s, %s)" % (doc, metadata, nodename, data)
result = doc.createElement(nodename)
if type(data) is list:
singular = metadata.get('plurals', {}).get(nodename, None)
--
cgit
From 659cb8bd43e2091c61f44dacf21274a677ea3146 Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Tue, 15 Mar 2011 23:35:44 -0400
Subject: openstack api 1.0 flavors resource now implemented; adding flavors
request value testing
---
nova/api/openstack/flavors.py | 12 ++++--
nova/tests/api/openstack/test_flavors.py | 71 +++++++++++++++++++++++++++++++-
2 files changed, 78 insertions(+), 5 deletions(-)
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index f3d040ba3..c99b945fb 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -22,6 +22,7 @@ from nova import context
from nova.api.openstack import faults
from nova.api.openstack import common
from nova.compute import instance_types
+from nova.api.openstack.views import flavors as flavors_views
from nova import wsgi
import nova.api.openstack
@@ -47,13 +48,18 @@ class Controller(wsgi.Controller):
def show(self, req, id):
"""Return data about the given flavor id."""
ctxt = req.environ['nova.context']
- values = db.instance_type_get_by_flavor_id(ctxt, id)
+ flavor = db.api.instance_type_get_by_flavor_id(ctxt, id)
+ values = {
+ "id": flavor["flavorid"],
+ "name": flavor["name"],
+ "ram": flavor["memory_mb"],
+ "disk": flavor["local_gb"],
+ }
return dict(flavor=values)
- raise faults.Fault(exc.HTTPNotFound())
def _all_ids(self, req):
"""Return the list of all flavorids."""
ctxt = req.environ['nova.context']
- inst_types = db.instance_type_get_all(ctxt)
+ inst_types = db.api.instance_type_get_all(ctxt)
flavor_ids = [inst_types[i]['flavorid'] for i in inst_types.keys()]
return sorted(flavor_ids)
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index 8280a505f..8f53d14cc 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -15,17 +15,38 @@
# License for the specific language governing permissions and limitations
# under the License.
+import json
import stubout
import webob
from nova import test
import nova.api
from nova import context
-from nova import db
from nova.api.openstack import flavors
+from nova import db
from nova.tests.api.openstack import fakes
+def stub_flavor(flavorid, name, memory_mb="256", local_gb="10"):
+ return {
+ "flavorid": str(flavorid),
+ "name": name,
+ "memory_mb": memory_mb,
+ "local_gb": local_gb,
+ }
+
+
+def return_instance_type_by_flavor_id(context, flavorid):
+ return stub_flavor(flavorid, "flavor %s" % (flavorid,))
+
+def return_instance_types(context, num=2):
+ instance_types = {}
+ for i in xrange(1,num+1):
+ name = "flavor %s" % (i,)
+ instance_types[name] = stub_flavor(i, name)
+ return instance_types
+
+
class FlavorsTest(test.TestCase):
def setUp(self):
super(FlavorsTest, self).setUp()
@@ -35,6 +56,10 @@ class FlavorsTest(test.TestCase):
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_auth(self.stubs)
+ self.stubs.Set(nova.db.api, "instance_type_get_all",
+ return_instance_types)
+ self.stubs.Set(nova.db.api, "instance_type_get_by_flavor_id",
+ return_instance_type_by_flavor_id)
self.context = context.get_admin_context()
def tearDown(self):
@@ -45,8 +70,50 @@ class FlavorsTest(test.TestCase):
req = webob.Request.blank('/v1.0/flavors')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
+ flavors = json.loads(res.body)["flavors"]
+ expected = [
+ {
+ "id": "1",
+ "name": "flavor 1",
+ },
+ {
+ "id": "2",
+ "name": "flavor 2",
+ },
+ ]
+ self.assertEqual(flavors, expected)
+
+ def test_get_flavor_list_detail(self):
+ req = webob.Request.blank('/v1.0/flavors/detail')
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ flavors = json.loads(res.body)["flavors"]
+ expected = [
+ {
+ "id": "1",
+ "name": "flavor 1",
+ "ram": "256",
+ "disk": "10",
+ },
+ {
+ "id": "2",
+ "name": "flavor 2",
+ "ram": "256",
+ "disk": "10",
+ },
+ ]
+ self.assertEqual(flavors, expected)
+
def test_get_flavor_by_id(self):
- req = webob.Request.blank('/v1.0/flavors/1')
+ req = webob.Request.blank('/v1.0/flavors/12')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
+ flavor = json.loads(res.body)["flavor"]
+ expected = {
+ "id": "12",
+ "name": "flavor 12",
+ "ram": "256",
+ "disk": "10",
+ }
+ self.assertEqual(flavor, expected)
--
cgit
From 33419a2f84ea6bfbf6ff47fb1f01ef0c21389a54 Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Wed, 16 Mar 2011 00:22:34 -0400
Subject: pep8 fixes
---
nova/tests/api/openstack/test_flavors.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index 8f53d14cc..4f504808c 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -39,9 +39,10 @@ def stub_flavor(flavorid, name, memory_mb="256", local_gb="10"):
def return_instance_type_by_flavor_id(context, flavorid):
return stub_flavor(flavorid, "flavor %s" % (flavorid,))
+
def return_instance_types(context, num=2):
instance_types = {}
- for i in xrange(1,num+1):
+ for i in xrange(1, num + 1):
name = "flavor %s" % (i,)
instance_types[name] = stub_flavor(i, name)
return instance_types
@@ -103,7 +104,6 @@ class FlavorsTest(test.TestCase):
},
]
self.assertEqual(flavors, expected)
-
def test_get_flavor_by_id(self):
req = webob.Request.blank('/v1.0/flavors/12')
--
cgit
From 78542ad1de6476a8962fa0c3b273c4a272410a83 Mon Sep 17 00:00:00 2001
From: Naveed Massjouni
Date: Wed, 16 Mar 2011 00:38:47 -0400
Subject: req envirom param 'nova.api.openstack.version' should be
'api.version'
---
nova/api/openstack/auth.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 7ae285019..6f1cf5e63 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -70,7 +70,7 @@ class AuthMiddleware(wsgi.Middleware):
req.environ['nova.context'] = context.RequestContext(user, account)
version = req.path.split('/')[1].replace('v', '')
- req.environ['nova.api.openstack.version'] = version
+ req.environ['api.version'] = version
return self.application
def has_authentication(self, req):
--
cgit
From 20031162372329b40ca90b1bc39cebb4f187cace Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Tue, 15 Mar 2011 23:22:17 -0700
Subject: Use integer ids for (fake) users
---
nova/tests/api/openstack/fakes.py | 3 +--
nova/tests/api/openstack/test_accounts.py | 4 ++--
nova/tests/api/openstack/test_users.py | 4 ++--
3 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 52ac80e3f..c2ae48ce4 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -240,8 +240,7 @@ class FakeAuthManager(object):
@classmethod
def reset_fake_data(cls):
- cls.auth_data = dict(acc1=User('guy1', 'guy1', 'acc1',
- 'fortytwo!', False))
+ cls.auth_data = dict(acc1=User(1, 'guy1', 'acc1', 'fortytwo!', False))
cls.projects = dict(testacct=Project('testacct',
'testacct',
'guy1',
diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py
index 1bf49b33b..5cb08ffd2 100644
--- a/nova/tests/api/openstack/test_accounts.py
+++ b/nova/tests/api/openstack/test_accounts.py
@@ -57,8 +57,8 @@ class AccountsTest(test.TestCase):
self.allow_admin = FLAGS.allow_admin_api
FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
- joeuser = User('guy1', 'guy1', 'acc1', 'fortytwo!', False)
- superuser = User('guy2', 'guy2', 'acc2', 'swordfish', True)
+ joeuser = User(1, 'guy1', 'acc1', 'fortytwo!', False)
+ superuser = User(2, 'guy2', 'acc2', 'swordfish', True)
fakemgr.add_user(joeuser)
fakemgr.add_user(superuser)
fakemgr.create_project('test1', joeuser)
diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py
index a62db7efc..652aac936 100644
--- a/nova/tests/api/openstack/test_users.py
+++ b/nova/tests/api/openstack/test_users.py
@@ -61,8 +61,8 @@ class UsersTest(test.TestCase):
self.allow_admin = FLAGS.allow_admin_api
FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
- fakemgr.add_user(User('guy1', 'guy1', 'acc1', 'fortytwo!', False))
- fakemgr.add_user(User('guy2', 'guy2', 'acc2', 'swordfish', True))
+ fakemgr.add_user(User(1, 'guy1', 'acc1', 'fortytwo!', False))
+ fakemgr.add_user(User(2, 'guy2', 'acc2', 'swordfish', True))
def tearDown(self):
self.stubs.UnsetAll()
--
cgit
From 8b3e35b157c688fd38d5aa0eb10ddef33653003d Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: Wed, 16 Mar 2011 10:29:04 +0100
Subject: fixed pep8 errors (with version 0.5.0)
---
bin/nova-manage | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 44b1d9ac6..c38e25d6b 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -739,8 +739,7 @@ class InstanceCommands(object):
_('project'),
_('user'),
_('zone'),
- _('index')
- )
+ _('index'))
if host == None:
instances = db.instance_get_all(context.get_admin_context())
@@ -762,8 +761,7 @@ class InstanceCommands(object):
instance['project_id'],
instance['user_id'],
instance['availability_zone'],
- instance['launch_index']
- )
+ instance['launch_index'])
class VolumeCommands(object):
@@ -1053,8 +1051,7 @@ CATEGORIES = [
('instance_type', InstanceTypeCommands),
('image', ImageCommands),
('flavor', InstanceTypeCommands),
- ('instance', InstanceCommands)
-]
+ ('instance', InstanceCommands)]
def lazy_match(name, key_value_tuples):
--
cgit
From 016669543a1f6d4ffc281637ba98c6b6fe30be82 Mon Sep 17 00:00:00 2001
From: Thierry Carrez
Date: Wed, 16 Mar 2011 10:38:48 +0100
Subject: Fix unknown exception error in euca-get-ajax-console
---
nova/virt/libvirt_conn.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 7994e9547..d7bdc3faa 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -502,7 +502,7 @@ class LibvirtConnection(object):
cmd = 'netcat', '0.0.0.0', port, '-w', '1'
try:
stdout, stderr = utils.execute(*cmd, process_input='')
- except ProcessExecutionError:
+ except exception.ProcessExecutionError:
return port
raise Exception(_('Unable to find an open port'))
--
cgit
From ba35831c1f66c424e9495642ba23e9d2742a339e Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: Wed, 16 Mar 2011 10:58:02 +0100
Subject: added correct path to cpu information (tested on a system with 1
installed cpu package)
---
nova/virt/libvirt_conn.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 7994e9547..9943b742a 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -984,18 +984,18 @@ class LibvirtConnection(object):
xml = self._conn.getCapabilities()
xml = libxml2.parseDoc(xml)
- nodes = xml.xpathEval('//cpu')
+ nodes = xml.xpathEval('//host/cpu')
if len(nodes) != 1:
raise exception.Invalid(_("Invalid xml. '' must be 1,"
"but %d\n") % len(nodes)
+ xml.serialize())
cpu_info = dict()
- cpu_info['arch'] = xml.xpathEval('//cpu/arch')[0].getContent()
- cpu_info['model'] = xml.xpathEval('//cpu/model')[0].getContent()
- cpu_info['vendor'] = xml.xpathEval('//cpu/vendor')[0].getContent()
+ cpu_info['arch'] = xml.xpathEval('//host/cpu/arch')[0].getContent()
+ cpu_info['model'] = xml.xpathEval('//host/cpu/model')[0].getContent()
+ cpu_info['vendor'] = xml.xpathEval('//host/cpu/vendor')[0].getContent()
- topology_node = xml.xpathEval('//cpu/topology')[0].get_properties()
+ topology_node = xml.xpathEval('//host/cpu/topology')[0].get_properties()
topology = dict()
while topology_node != None:
name = topology_node.get_name()
@@ -1009,7 +1009,7 @@ class LibvirtConnection(object):
raise exception.Invalid(_("Invalid xml: topology(%(topology)s) "
"must have %(ks)s") % locals())
- feature_nodes = xml.xpathEval('//cpu/feature')
+ feature_nodes = xml.xpathEval('//host/cpu/feature')
features = list()
for nodes in feature_nodes:
features.append(nodes.get_properties().getContent())
--
cgit
From e2f8c143eab3b43acd9e3658a480f75f435e7d42 Mon Sep 17 00:00:00 2001
From: Koji Iida
Date: Wed, 16 Mar 2011 19:51:06 +0900
Subject: Updating gateway_v6 in _on_set_network_host() is not required for
FlatManager
---
nova/network/manager.py | 5 -----
1 file changed, 5 deletions(-)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index c51eb9ad0..7e28e6da2 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -404,11 +404,6 @@ class FlatManager(NetworkManager):
net = {}
net['injected'] = FLAGS.flat_injected
net['dns'] = FLAGS.flat_network_dns
- if not FLAGS.fake_network:
- if(FLAGS.use_ipv6):
- net['gateway_v6'] = \
- utils.get_my_linklocal(
- FLAGS.flat_network_bridge)
self.db.network_update(context, network_id, net)
def allocate_floating_ip(self, context, project_id):
--
cgit
From af2cae27930a3983c96a0b1705f828d65d4829cd Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Wed, 16 Mar 2011 12:18:15 +0100
Subject: Fix a couple of things that assume that libvirt == kvm/qemu.
---
bin/nova-manage | 4 +++-
nova/virt/libvirt_conn.py | 42 +++++++++++++++++++++++++++---------------
2 files changed, 30 insertions(+), 16 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 2b42dfff5..c84891619 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -574,7 +574,9 @@ class VmCommands(object):
ctxt = context.get_admin_context()
instance_id = ec2utils.ec2_id_to_id(ec2_id)
- if FLAGS.connection_type != 'libvirt':
+ if (FLAGS.connection_type != 'libvirt' or
+ (FLAGS.connection_type == 'libvirt' and
+ FLAGS.libvirt_type not in ['kvm', 'qemu'])):
msg = _('Only KVM is supported for now. Sorry!')
raise exception.Error(msg)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 7994e9547..96463d0f2 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -991,23 +991,35 @@ class LibvirtConnection(object):
+ xml.serialize())
cpu_info = dict()
- cpu_info['arch'] = xml.xpathEval('//cpu/arch')[0].getContent()
- cpu_info['model'] = xml.xpathEval('//cpu/model')[0].getContent()
- cpu_info['vendor'] = xml.xpathEval('//cpu/vendor')[0].getContent()
- topology_node = xml.xpathEval('//cpu/topology')[0].get_properties()
+ arch_nodes = xml.xpathEval('//cpu/arch')
+ if len(arch_nodes):
+ cpu_info['arch'] = arch_nodes[0].getContent()
+
+ model_nodes = xml.xpathEval('//cpu/model')
+ if len(model_nodes):
+ cpu_info['model'] = model_nodes[0].getContent()
+
+ vendor_nodes = xml.xpathEval('//cpu/vendor')
+ if len(vendor_nodes):
+ cpu_info['vendor'] = vendor_nodes[0].getContent()
+
+ topology_nodes = xml.xpathEval('//cpu/topology')
topology = dict()
- while topology_node != None:
- name = topology_node.get_name()
- topology[name] = topology_node.getContent()
- topology_node = topology_node.get_next()
-
- keys = ['cores', 'sockets', 'threads']
- tkeys = topology.keys()
- if list(set(tkeys)) != list(set(keys)):
- ks = ', '.join(keys)
- raise exception.Invalid(_("Invalid xml: topology(%(topology)s) "
- "must have %(ks)s") % locals())
+ if len(topology_nodes):
+ topology_node = topology_nodes[0].get_properties()
+ while topology_node != None:
+ name = topology_node.get_name()
+ topology[name] = topology_node.getContent()
+ topology_node = topology_node.get_next()
+
+ keys = ['cores', 'sockets', 'threads']
+ tkeys = topology.keys()
+ if list(set(tkeys)) != list(set(keys)):
+ ks = ', '.join(keys)
+ raise exception.Invalid(_("Invalid xml: topology(%(topology)s) "
+ "must have %(ks)s") % locals())
+
feature_nodes = xml.xpathEval('//cpu/feature')
features = list()
--
cgit
From 8964cbecc10885bc6eff08544d62db1747fb14ef Mon Sep 17 00:00:00 2001
From: Koji Iida
Date: Wed, 16 Mar 2011 20:24:20 +0900
Subject: pep8 clean
---
nova/db/sqlalchemy/migrate_repo/versions/012_add_ipv6_flatmanager.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
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 be1edc8f6..9f98f436f 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
@@ -83,8 +83,7 @@ networks = Table('networks', meta,
Column(
'label',
String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False))
- )
+ unicode_error=None, _warn_on_bytestring=False)))
fixed_ips = Table('fixed_ips', meta,
Column('created_at', DateTime(timezone=False)),
--
cgit
From 2e81ce6bb5b1083220e7ae5c17113fd44465ddbf Mon Sep 17 00:00:00 2001
From: Koji Iida
Date: Wed, 16 Mar 2011 21:17:19 +0900
Subject: Fix instance creation fail under use_ipv6=false and FlatManager
---
nova/virt/libvirt_conn.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 9efbb3342..a850bad87 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -660,11 +660,10 @@ class LibvirtConnection(object):
if network_ref['injected']:
admin_context = context.get_admin_context()
address = db.instance_get_fixed_address(admin_context, inst['id'])
- address_v6 = db.instance_get_fixed_address_v6(admin_context,
- inst['id'])
- gateway_v6 = network_ref['gateway_v6']
- if not gateway_v6:
- gateway_v6 = "fd00::"
+ address_v6 = None
+ if FLAGS.use_ipv6:
+ address_v6 = db.instance_get_fixed_address_v6(admin_context,
+ inst['id'])
interfaces_info = {'address': address,
'netmask': network_ref['netmask'],
--
cgit
From a586714557e38116b6b4f473aa21ac54ff0223e7 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Wed, 16 Mar 2011 10:10:58 -0400
Subject: Added i18n to error message.
---
nova/api/openstack/limits.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index c4e04e9d9..1fe519f8a 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -225,7 +225,7 @@ class RateLimitingMiddleware(Middleware):
delay, error = self._limiter.check_for_delay(verb, url, username)
if delay:
- msg = "This request was rate-limited."
+ msg = _("This request was rate-limited.")
retry = time.time() + delay
return faults.OverLimitFault(msg, error, retry)
--
cgit
From 5473f3a47c1b11c6625960e1ed73c28c7b061fcb Mon Sep 17 00:00:00 2001
From: Brian Waldon
Date: Wed, 16 Mar 2011 13:41:00 -0400
Subject: moving code out of try/except that would never trigger NotFound
---
nova/api/openstack/servers.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index dc62882eb..818dd825f 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -57,11 +57,12 @@ class Controller(wsgi.Controller):
def ips(self, req, id):
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
- builder = addresses_views.get_view_builder(req)
- return builder.build(instance)
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
+ builder = addresses_views.get_view_builder(req)
+ return builder.build(instance)
+
def index(self, req):
""" Returns a list of server names and ids for a given user """
return self._items(req, is_detail=False)
--
cgit
From 5379f3654e04a0443f3237623f772a17f13e9d90 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Wed, 16 Mar 2011 12:44:38 -0500
Subject: refactored, bugfixes
---
nova/virt/xenapi/vm_utils.py | 4 +-
nova/virt/xenapi/vmops.py | 163 +++++++++++++++++++------------------------
2 files changed, 72 insertions(+), 95 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index f07b57796..1f03b4124 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -234,11 +234,11 @@ class VMHelper(HelperBase):
raise StorageError(_('Unable to destroy VBD %s') % vbd_ref)
@classmethod
- def create_vif(cls, session, vm_ref, network_ref, mac_address, dev="0"):
+ def create_vif(cls, session, vm_ref, network_ref, mac_address, dev):
"""Create a VIF record. Returns a Deferred that gives the new
VIF reference."""
vif_rec = {}
- vif_rec['device'] = dev
+ vif_rec['device'] = str(dev)
vif_rec['network'] = network_ref
vif_rec['VM'] = vm_ref
vif_rec['MAC'] = mac_address
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 64f2c6231..485dd41ca 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -128,13 +128,17 @@ class VMOps(object):
vdi_ref=vdi_ref, userdevice=0, bootable=True)
# inject_network_info and create vifs
- if network_info is not None:
- self.inject_network_info(instance, network_info)
- self.create_vifs(instance, [nw for (nw, mapping) in network_info])
- else:
- # TODO(tr3buchet) - goes away with multi-nic
- networks = self.inject_network_info(instance)
- self.create_vifs(instance, networks)
+ # TODO(tr3buchet) - check to make sure we have network info, otherwise
+ # create it now. This goes away once nova-multi-nic hits.
+ if network_info is None:
+ admin_context = context.get_admin_context()
+ IPs = db.fixed_ip_get_all_by_instance(admin_context,
+ instance['id'])
+ networks = db.network_get_all_by_instance(admin_context,
+ instance['id'])
+ network_info = self._get_network_info(instance, networks, IPs)
+ self.inject_network_info(vm_ref, network_info)
+ self.create_vifs(vm_ref, network_info)
LOG.debug(_('Starting VM %s...'), vm_ref)
self._start(instance, vm_ref)
@@ -689,104 +693,77 @@ class VMOps(object):
# TODO: implement this!
return 'http://fakeajaxconsole/fake_url'
- def inject_network_info(self, instance, network_info=None):
+ # TODO(tr3buchet) - remove this function after nova multi-nic
+ def _get_network_info(self, instance, networks, IPs):
+ """creates network info list for instance"""
+
+ tuple_list = []
+ for network in networks:
+ network_IPs = [ip for ip in IPs if ip.network_id == network.id]
+
+ def ip_dict(ip):
+ return {
+ "ip": ip.address,
+ "netmask": network["netmask"],
+ "enabled": "1"}
+
+ def ip6_dict(ip6):
+ return {
+ "ip": ip6.addressV6,
+ "netmask": ip6.netmaskV6,
+ "gateway": ip6.gatewayV6,
+ "enabled": "1"}
+
+ mac_id = instance.mac_address.replace(':', '')
+ location = 'vm-data/networking/%s' % mac_id
+ info = {
+ 'label': network['label'],
+ 'gateway': network['gateway'],
+ 'mac': instance.mac_address,
+ 'dns': [network['dns']],
+ 'ips': [ip_dict(ip) for ip in network_IPs],
+ 'ip6s': [ip6_dict(ip) for ip in network_IPs]}
+ tuple_list.append((network, info))
+
+ def inject_network_info(self, vm_ref, network_info):
"""
Generate the network info and make calls to place it into the
xenstore and the xenstore param list
-
"""
- vm_ref = self._get_vm_opaque_ref(instance.id)
- logging.debug(_("injecting network info to xenstore for vm: |%s|"),
- vm_ref)
- if network_info is not None:
- for (network, mapping) in network_info:
- self.write_to_param_xenstore(vm_ref, {location: mapping})
- try:
- self.write_to_xenstore(vm_ref, location,
- mapping['location'])
- except KeyError:
- # catch KeyError for domid if instance isn't running
- pass
- else:
- # TODO(tr3buchet) - this bit here when network_info is None goes
- # away with multi-nic
- admin_context = context.get_admin_context()
- IPs = db.fixed_ip_get_all_by_instance(admin_context,
- instance['id'])
- networks = db.network_get_all_by_instance(admin_context,
- instance['id'])
- for network in networks:
- network_IPs = [ip for ip in IPs if ip.network_id == network.id]
-
- def ip_dict(ip):
- return {
- "ip": ip.address,
- "netmask": network["netmask"],
- "enabled": "1"}
-
- def ip6_dict(ip6):
- return {
- "ip": ip6.addressV6,
- "netmask": ip6.netmaskV6,
- "gateway": ip6.gatewayV6,
- "enabled": "1"}
-
- mac_id = instance.mac_address.replace(':', '')
- location = 'vm-data/networking/%s' % mac_id
- mapping = {
- 'label': network['label'],
- 'gateway': network['gateway'],
- 'mac': instance.mac_address,
- 'dns': [network['dns']],
- 'ips': [ip_dict(ip) for ip in network_IPs],
- 'ip6s': [ip6_dict(ip) for ip in network_IPs]}
-
- self.write_to_param_xenstore(vm_ref, {location: mapping})
-
- try:
- self.write_to_xenstore(vm_ref, location,
- mapping['location'])
- except KeyError:
- # catch KeyError for domid if instance isn't running
- pass
-
- return networks
-
- def create_vifs(self, instance, networks=None):
- """
- Creates vifs for an instance
+ logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref)
- """
- vm_ref = self._get_vm_opaque_ref(instance.id)
+ # make sure we have a vm opaque ref (raises otherwise)
+ self._session.get_xenapi().VM.get_record(vm_ref)
+
+ for (network, info) in network_info:
+ location = 'vm-data/networking/%s' % info['mac'].replace(':', '')
+ self.write_to_param_xenstore(vm_ref, {location: info})
+ try:
+ self.write_to_xenstore(vm_ref, location, info)
+ except KeyError:
+ # catch KeyError for domid if instance isn't running
+ pass
+
+ def create_vifs(self, vm_ref, network_info):
+ """Creates vifs for an instance"""
logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
- # TODO(tr3buchet) - goes away with multi-nic
- if networks is None:
- networks = db.network_get_all_by_instance(admin_context,
- instance['id'])
- # TODO(tr3buchet) - remove comment in multi-nic
- # this bit here about creating the vifs will be updated
- # in multi-nic to handle multiple IPs on the same network
- # and multiple networks
- # for now it works as there is only one of each
- for network in networks:
+
+ # make sure we have a vm opaque ref (raises otherwise)
+ self._session.get_xenapi().VM.get_record(vm_ref)
+
+ device = 0
+ for (network, info) in networks:
+ mac_address = info['mac']
bridge = network['bridge']
network_ref = \
NetworkHelper.find_network_with_bridge(self._session, bridge)
- if network_ref:
- try:
- device = "1" if instance._rescue else "0"
- except AttributeError:
- device = "0"
-
- VMHelper.create_vif(self._session, vm_ref, network_ref,
- instance.mac_address, device)
+ VMHelper.create_vif(self._session, vm_ref, network_ref,
+ mac_address, device)
+ device += 1
def reset_network(self, instance):
- """
- Creates uuid arg to pass to make_agent_call and calls it.
-
- """
+ """Creates uuid arg to pass to make_agent_call and calls it."""
args = {'id': str(uuid.uuid4())}
resp = self._make_agent_call('resetnetwork', instance, '', args)
--
cgit
From d418926b514372f0f48922024e600bafcc657fd9 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Wed, 16 Mar 2011 12:50:11 -0500
Subject: forgot to return network info - teehee
---
nova/virt/xenapi/vmops.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 485dd41ca..27f9a3a17 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -697,7 +697,7 @@ class VMOps(object):
def _get_network_info(self, instance, networks, IPs):
"""creates network info list for instance"""
- tuple_list = []
+ network_info = []
for network in networks:
network_IPs = [ip for ip in IPs if ip.network_id == network.id]
@@ -714,8 +714,6 @@ class VMOps(object):
"gateway": ip6.gatewayV6,
"enabled": "1"}
- mac_id = instance.mac_address.replace(':', '')
- location = 'vm-data/networking/%s' % mac_id
info = {
'label': network['label'],
'gateway': network['gateway'],
@@ -723,7 +721,8 @@ class VMOps(object):
'dns': [network['dns']],
'ips': [ip_dict(ip) for ip in network_IPs],
'ip6s': [ip6_dict(ip) for ip in network_IPs]}
- tuple_list.append((network, info))
+ network_info.append((network, info))
+ return network_info
def inject_network_info(self, vm_ref, network_info):
"""
@@ -752,7 +751,7 @@ class VMOps(object):
self._session.get_xenapi().VM.get_record(vm_ref)
device = 0
- for (network, info) in networks:
+ for (network, info) in network_info:
mac_address = info['mac']
bridge = network['bridge']
network_ref = \
--
cgit
From 7fa96f6292ff7d63621fe024b1ef45b1a1996121 Mon Sep 17 00:00:00 2001
From: "jaypipes@gmail.com" <>
Date: Wed, 16 Mar 2011 14:49:18 -0400
Subject: Re-commit r804
---
nova/tests/api/openstack/test_servers.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 40026a615..cde2fc036 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -216,7 +216,7 @@ class ServersTest(test.TestCase):
servers = json.loads(res.body)['servers']
self.assertEqual([s['id'] for s in servers], [1, 2])
- def _test_create_instance_helper(self, with_key_pair):
+ def _test_create_instance_helper(self):
def instance_create(context, inst):
return {'id': '1', 'display_name': 'server_test'}
@@ -272,11 +272,11 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_create_instance(self):
- self._test_create_instance_helper(True)
+ self._test_create_instance_helper()
def test_create_instance_no_key_pair(self):
- fakes.stub_out_key_pair_funcs(self.stubs, False)
- self._test_create_instance_helper(False)
+ fakes.stub_out_key_pair_funcs(self.stubs, have_key_pair=False)
+ self._test_create_instance_helper()
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
--
cgit
From 663c1726d9a96540b8fd729223fcb34d7cf3cdf7 Mon Sep 17 00:00:00 2001
From: "jaypipes@gmail.com" <>
Date: Wed, 16 Mar 2011 14:49:25 -0400
Subject: Re-commit r805
---
nova/tests/api/openstack/test_servers.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index cde2fc036..ad36fa551 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -217,6 +217,7 @@ class ServersTest(test.TestCase):
self.assertEqual([s['id'] for s in servers], [1, 2])
def _test_create_instance_helper(self):
+ """Shared implementation for tests below that create instance"""
def instance_create(context, inst):
return {'id': '1', 'display_name': 'server_test'}
--
cgit
From 157ea09c03148ff4615bae27ca3f276a05620825 Mon Sep 17 00:00:00 2001
From: Christian Berendt
Date: Wed, 16 Mar 2011 19:54:15 +0100
Subject: fixed pep8 issue
---
nova/virt/libvirt_conn.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 9943b742a..4e17555f4 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -995,7 +995,8 @@ class LibvirtConnection(object):
cpu_info['model'] = xml.xpathEval('//host/cpu/model')[0].getContent()
cpu_info['vendor'] = xml.xpathEval('//host/cpu/vendor')[0].getContent()
- topology_node = xml.xpathEval('//host/cpu/topology')[0].get_properties()
+ topology_node = xml.xpathEval('//host/cpu/topology')[0]\
+ .get_properties()
topology = dict()
while topology_node != None:
name = topology_node.get_name()
--
cgit
From 7de1ef791296d547c2691454d5cb5451087cd76b Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Wed, 16 Mar 2011 12:15:57 -0700
Subject: User ids are strings, and are not necessarily == name. Also fix so
that non-existent user gives a 404, not a 500.
---
nova/api/openstack/users.py | 17 +++++++--
nova/auth/manager.py | 11 +++++-
nova/tests/api/openstack/fakes.py | 4 +--
nova/tests/api/openstack/test_accounts.py | 22 ++++++------
nova/tests/api/openstack/test_auth.py | 8 ++---
nova/tests/api/openstack/test_users.py | 58 +++++++++++++++++++++----------
6 files changed, 79 insertions(+), 41 deletions(-)
diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py
index ebd0f4512..d3ab3d553 100644
--- a/nova/api/openstack/users.py
+++ b/nova/api/openstack/users.py
@@ -13,13 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
-import common
+from webob import exc
from nova import exception
from nova import flags
from nova import log as logging
from nova import wsgi
-
+from nova.api.openstack import common
+from nova.api.openstack import faults
from nova.auth import manager
FLAGS = flags.FLAGS
@@ -63,7 +64,17 @@ class Controller(wsgi.Controller):
def show(self, req, id):
"""Return data about the given user id"""
- user = self.manager.get_user(id)
+
+ #NOTE(justinsb): The drivers are a little inconsistent in how they
+ # deal with "NotFound" - some throw, some return None.
+ try:
+ user = self.manager.get_user(id)
+ except exception.NotFound:
+ user = None
+
+ if user is None:
+ raise faults.Fault(exc.HTTPNotFound())
+
return dict(user=_translate_keys(user))
def delete(self, req, id):
diff --git a/nova/auth/manager.py b/nova/auth/manager.py
index 450ab803a..793499629 100644
--- a/nova/auth/manager.py
+++ b/nova/auth/manager.py
@@ -96,10 +96,19 @@ class AuthBase(object):
class User(AuthBase):
- """Object representing a user"""
+ """Object representing a user
+
+ The following attributes are defined:
+ :id: A system identifier for the user. A string (for LDAP)
+ :name: The user name, potentially in some more friendly format
+ :access: The 'username' for EC2 authentication
+ :secret: The 'password' for EC2 authenticatoin
+ :admin: ???
+ """
def __init__(self, id, name, access, secret, admin):
AuthBase.__init__(self)
+ assert isinstance(id, basestring)
self.id = id
self.name = name
self.access = access
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index c2ae48ce4..5decb2bad 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -240,10 +240,10 @@ class FakeAuthManager(object):
@classmethod
def reset_fake_data(cls):
- cls.auth_data = dict(acc1=User(1, 'guy1', 'acc1', 'fortytwo!', False))
+ cls.auth_data = dict(u1=User('id1', 'guy1', 'acc1', 'secret1', False))
cls.projects = dict(testacct=Project('testacct',
'testacct',
- 'guy1',
+ 'id1',
'test',
[]))
diff --git a/nova/tests/api/openstack/test_accounts.py b/nova/tests/api/openstack/test_accounts.py
index 5cb08ffd2..64abcf48c 100644
--- a/nova/tests/api/openstack/test_accounts.py
+++ b/nova/tests/api/openstack/test_accounts.py
@@ -19,11 +19,9 @@ import json
import stubout
import webob
-import nova.api
-import nova.api.openstack.auth
-from nova import context
from nova import flags
from nova import test
+from nova.api.openstack import accounts
from nova.auth.manager import User
from nova.tests.api.openstack import fakes
@@ -44,9 +42,9 @@ class AccountsTest(test.TestCase):
def setUp(self):
super(AccountsTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
- self.stubs.Set(nova.api.openstack.accounts.Controller, '__init__',
+ self.stubs.Set(accounts.Controller, '__init__',
fake_init)
- self.stubs.Set(nova.api.openstack.accounts.Controller, '_check_admin',
+ self.stubs.Set(accounts.Controller, '_check_admin',
fake_admin_check)
fakes.FakeAuthManager.clear_fakes()
fakes.FakeAuthDatabase.data = {}
@@ -57,8 +55,8 @@ class AccountsTest(test.TestCase):
self.allow_admin = FLAGS.allow_admin_api
FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
- joeuser = User(1, 'guy1', 'acc1', 'fortytwo!', False)
- superuser = User(2, 'guy2', 'acc2', 'swordfish', True)
+ joeuser = User('id1', 'guy1', 'acc1', 'secret1', False)
+ superuser = User('id2', 'guy2', 'acc2', 'secret2', True)
fakemgr.add_user(joeuser)
fakemgr.add_user(superuser)
fakemgr.create_project('test1', joeuser)
@@ -76,7 +74,7 @@ class AccountsTest(test.TestCase):
self.assertEqual(res_dict['account']['id'], 'test1')
self.assertEqual(res_dict['account']['name'], 'test1')
- self.assertEqual(res_dict['account']['manager'], 'guy1')
+ self.assertEqual(res_dict['account']['manager'], 'id1')
self.assertEqual(res.status_int, 200)
def test_account_delete(self):
@@ -88,7 +86,7 @@ class AccountsTest(test.TestCase):
def test_account_create(self):
body = dict(account=dict(description='test account',
- manager='guy1'))
+ manager='id1'))
req = webob.Request.blank('/v1.0/accounts/newacct')
req.headers["Content-Type"] = "application/json"
req.method = 'PUT'
@@ -101,14 +99,14 @@ class AccountsTest(test.TestCase):
self.assertEqual(res_dict['account']['id'], 'newacct')
self.assertEqual(res_dict['account']['name'], 'newacct')
self.assertEqual(res_dict['account']['description'], 'test account')
- self.assertEqual(res_dict['account']['manager'], 'guy1')
+ self.assertEqual(res_dict['account']['manager'], 'id1')
self.assertTrue('newacct' in
fakes.FakeAuthManager.projects)
self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3)
def test_account_update(self):
body = dict(account=dict(description='test account',
- manager='guy2'))
+ manager='id2'))
req = webob.Request.blank('/v1.0/accounts/test1')
req.headers["Content-Type"] = "application/json"
req.method = 'PUT'
@@ -121,5 +119,5 @@ class AccountsTest(test.TestCase):
self.assertEqual(res_dict['account']['id'], 'test1')
self.assertEqual(res_dict['account']['name'], 'test1')
self.assertEqual(res_dict['account']['description'], 'test account')
- self.assertEqual(res_dict['account']['manager'], 'guy2')
+ self.assertEqual(res_dict['account']['manager'], 'id2')
self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index e1f936bb1..446c5c149 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -51,7 +51,7 @@ class Test(test.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'user1', 'user1_key', None, None)
+ u = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
f.add_user(u)
req = webob.Request.blank('/v1.0/')
@@ -66,7 +66,7 @@ class Test(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'user1', 'user1_key', None, None)
+ u = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
f.add_user(u)
f.create_project('user1_project', u)
@@ -124,7 +124,7 @@ class Test(test.TestCase):
def test_bad_user_good_key(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'user1', 'user1_key', None, None)
+ u = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
f.add_user(u)
req = webob.Request.blank('/v1.0/')
@@ -190,7 +190,7 @@ class TestLimiter(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User(1, 'user1', 'user1_key', None, None)
+ u = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
f.add_user(u)
f.create_project('test', u)
diff --git a/nova/tests/api/openstack/test_users.py b/nova/tests/api/openstack/test_users.py
index 652aac936..effb2f592 100644
--- a/nova/tests/api/openstack/test_users.py
+++ b/nova/tests/api/openstack/test_users.py
@@ -18,11 +18,10 @@ import json
import stubout
import webob
-import nova.api
-import nova.api.openstack.auth
-from nova import context
from nova import flags
from nova import test
+from nova import utils
+from nova.api.openstack import users
from nova.auth.manager import User, Project
from nova.tests.api.openstack import fakes
@@ -43,14 +42,14 @@ class UsersTest(test.TestCase):
def setUp(self):
super(UsersTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
- self.stubs.Set(nova.api.openstack.users.Controller, '__init__',
+ self.stubs.Set(users.Controller, '__init__',
fake_init)
- self.stubs.Set(nova.api.openstack.users.Controller, '_check_admin',
+ self.stubs.Set(users.Controller, '_check_admin',
fake_admin_check)
fakes.FakeAuthManager.clear_fakes()
fakes.FakeAuthManager.projects = dict(testacct=Project('testacct',
'testacct',
- 'guy1',
+ 'id1',
'test',
[]))
fakes.FakeAuthDatabase.data = {}
@@ -61,8 +60,8 @@ class UsersTest(test.TestCase):
self.allow_admin = FLAGS.allow_admin_api
FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
- fakemgr.add_user(User(1, 'guy1', 'acc1', 'fortytwo!', False))
- fakemgr.add_user(User(2, 'guy2', 'acc2', 'swordfish', True))
+ fakemgr.add_user(User('id1', 'guy1', 'acc1', 'secret1', False))
+ fakemgr.add_user(User('id2', 'guy2', 'acc2', 'secret2', True))
def tearDown(self):
self.stubs.UnsetAll()
@@ -78,28 +77,44 @@ class UsersTest(test.TestCase):
self.assertEqual(len(res_dict['users']), 2)
def test_get_user_by_id(self):
- req = webob.Request.blank('/v1.0/users/guy2')
+ req = webob.Request.blank('/v1.0/users/id2')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
- self.assertEqual(res_dict['user']['id'], 'guy2')
+ self.assertEqual(res_dict['user']['id'], 'id2')
self.assertEqual(res_dict['user']['name'], 'guy2')
- self.assertEqual(res_dict['user']['secret'], 'swordfish')
+ self.assertEqual(res_dict['user']['secret'], 'secret2')
self.assertEqual(res_dict['user']['admin'], True)
self.assertEqual(res.status_int, 200)
def test_user_delete(self):
- req = webob.Request.blank('/v1.0/users/guy1')
+ # Check the user exists
+ req = webob.Request.blank('/v1.0/users/id1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+
+ self.assertEqual(res_dict['user']['id'], 'id1')
+ self.assertEqual(res.status_int, 200)
+
+ # Delete the user
+ req = webob.Request.blank('/v1.0/users/id1')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
- self.assertTrue('guy1' not in [u.id for u in
+ self.assertTrue('id1' not in [u.id for u in
fakes.FakeAuthManager.auth_data])
self.assertEqual(res.status_int, 200)
+ # Check the user is not returned (and returns 404)
+ req = webob.Request.blank('/v1.0/users/id1')
+ res = req.get_response(fakes.wsgi_app())
+ res_dict = json.loads(res.body)
+ self.assertEqual(res.status_int, 404)
+
def test_user_create(self):
+ secret = utils.generate_password()
body = dict(user=dict(name='test_guy',
access='acc3',
- secret='invasionIsInNormandy',
+ secret=secret,
admin=True))
req = webob.Request.blank('/v1.0/users')
req.headers["Content-Type"] = "application/json"
@@ -110,20 +125,25 @@ class UsersTest(test.TestCase):
res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
+
+ # NOTE(justinsb): This is a questionable assertion in general
+ # fake sets id=name, but others might not...
self.assertEqual(res_dict['user']['id'], 'test_guy')
+
self.assertEqual(res_dict['user']['name'], 'test_guy')
self.assertEqual(res_dict['user']['access'], 'acc3')
- self.assertEqual(res_dict['user']['secret'], 'invasionIsInNormandy')
+ self.assertEqual(res_dict['user']['secret'], secret)
self.assertEqual(res_dict['user']['admin'], True)
self.assertTrue('test_guy' in [u.id for u in
fakes.FakeAuthManager.auth_data])
self.assertEqual(len(fakes.FakeAuthManager.auth_data), 3)
def test_user_update(self):
+ new_secret = utils.generate_password()
body = dict(user=dict(name='guy2',
access='acc2',
- secret='invasionIsInNormandy'))
- req = webob.Request.blank('/v1.0/users/guy2')
+ secret=new_secret))
+ req = webob.Request.blank('/v1.0/users/id2')
req.headers["Content-Type"] = "application/json"
req.method = 'PUT'
req.body = json.dumps(body)
@@ -132,8 +152,8 @@ class UsersTest(test.TestCase):
res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
- self.assertEqual(res_dict['user']['id'], 'guy2')
+ self.assertEqual(res_dict['user']['id'], 'id2')
self.assertEqual(res_dict['user']['name'], 'guy2')
self.assertEqual(res_dict['user']['access'], 'acc2')
- self.assertEqual(res_dict['user']['secret'], 'invasionIsInNormandy')
+ self.assertEqual(res_dict['user']['secret'], new_secret)
self.assertEqual(res_dict['user']['admin'], True)
--
cgit
From 85bae497aa803914d329f2872d343a9982dc370e Mon Sep 17 00:00:00 2001
From: Cerberus
Date: Wed, 16 Mar 2011 14:35:04 -0500
Subject: Changes
---
nova/api/openstack/flavors.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index f3d040ba3..b9e40371d 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -36,7 +36,7 @@ class Controller(wsgi.Controller):
def index(self, req):
"""Return all flavors in brief."""
- return dict(flavors=[dict(id=flavor['id'], name=flavor['name'])
+ return dict(flavors=[dict(id=flavor['flavorid'], name=flavor['name'])
for flavor in self.detail(req)['flavors']])
def detail(self, req):
@@ -48,6 +48,7 @@ class Controller(wsgi.Controller):
"""Return data about the given flavor id."""
ctxt = req.environ['nova.context']
values = db.instance_type_get_by_flavor_id(ctxt, id)
+ values.update({'id': values['flavorid']})
return dict(flavor=values)
raise faults.Fault(exc.HTTPNotFound())
--
cgit
From 227957e31d75b24bb8afa078c8d3f2bc447a8215 Mon Sep 17 00:00:00 2001
From: "matt.dietz@rackspace.com" <>
Date: Wed, 16 Mar 2011 19:40:16 +0000
Subject: Unit test update
---
nova/tests/api/openstack/test_flavors.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/nova/tests/api/openstack/test_flavors.py b/nova/tests/api/openstack/test_flavors.py
index 8280a505f..30326dc50 100644
--- a/nova/tests/api/openstack/test_flavors.py
+++ b/nova/tests/api/openstack/test_flavors.py
@@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import json
import stubout
import webob
@@ -50,3 +51,5 @@ class FlavorsTest(test.TestCase):
req = webob.Request.blank('/v1.0/flavors/1')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
+ body = json.loads(res.body)
+ self.assertEqual(body['flavor']['id'], 1)
--
cgit
From f17fb9370d4af42267837a36c937f213669b0291 Mon Sep 17 00:00:00 2001
From: Cerberus
Date: Wed, 16 Mar 2011 14:43:57 -0500
Subject: Dumb
---
nova/api/openstack/flavors.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index b9e40371d..1c440b3a9 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -48,7 +48,7 @@ class Controller(wsgi.Controller):
"""Return data about the given flavor id."""
ctxt = req.environ['nova.context']
values = db.instance_type_get_by_flavor_id(ctxt, id)
- values.update({'id': values['flavorid']})
+ values['id'] = values['flavorid']
return dict(flavor=values)
raise faults.Fault(exc.HTTPNotFound())
--
cgit
From d95187aaf144cb40558f48d584a6bb8e07c6937d Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Wed, 16 Mar 2011 14:13:57 -0700
Subject: converted new lines from CRLF to LF
---
.../versions/012_add_ipv6_flatmanager.py | 302 +++++++++----------
nova/tests/network/__init__.py | 94 +++---
nova/tests/network/base.py | 308 ++++++++++----------
nova/tests/test_flat_network.py | 322 ++++++++++-----------
4 files changed, 513 insertions(+), 513 deletions(-)
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 9f98f436f..5f5e3d007 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
@@ -1,151 +1,151 @@
-# Copyright 2010 OpenStack LLC.
-# 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.
-
-from sqlalchemy import *
-from migrate import *
-
-from nova import log as logging
-
-
-meta = MetaData()
-
-
-# Table stub-definitions
-# Just for the ForeignKey and column creation to succeed, these are not the
-# actual definitions of instances or services.
-#
-
-#
-# Tables to alter
-#
-networks = Table('networks', meta,
- Column('created_at', DateTime(timezone=False)),
- Column('updated_at', DateTime(timezone=False)),
- Column('deleted_at', DateTime(timezone=False)),
- Column('deleted', Boolean(create_constraint=True, name=None)),
- Column('id', Integer(), primary_key=True, nullable=False),
- Column('injected', Boolean(create_constraint=True, name=None)),
- Column('cidr',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('netmask',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('bridge',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('gateway',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('broadcast',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('dns',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('vlan', Integer()),
- Column('vpn_public_address',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('vpn_public_port', Integer()),
- Column('vpn_private_address',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('dhcp_start',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('project_id',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('host',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('cidr_v6',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('ra_server', String(length=255,
- convert_unicode=False,
- assert_unicode=None,
- unicode_error=None,
- _warn_on_bytestring=False)),
- Column(
- 'label',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)))
-
-fixed_ips = Table('fixed_ips', meta,
- Column('created_at', DateTime(timezone=False)),
- Column('updated_at', DateTime(timezone=False)),
- Column('deleted_at', DateTime(timezone=False)),
- Column('deleted', Boolean(create_constraint=True, name=None)),
- Column('id', Integer(), primary_key=True, nullable=False),
- Column('address',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False)),
- Column('network_id',
- Integer(),
- ForeignKey('networks.id'),
- nullable=True),
- Column('instance_id',
- Integer(),
- ForeignKey('instances.id'),
- nullable=True),
- Column('allocated', Boolean(create_constraint=True, name=None)),
- Column('leased', Boolean(create_constraint=True, name=None)),
- Column('reserved', Boolean(create_constraint=True, name=None)),
- Column("addressV6", String(length=255,
- convert_unicode=False,
- assert_unicode=None,
- unicode_error=None,
- _warn_on_bytestring=False)),
- Column("netmaskV6", String(length=3,
- convert_unicode=False,
- assert_unicode=None,
- unicode_error=None,
- _warn_on_bytestring=False)),
- Column("gatewayV6", String(length=255,
- convert_unicode=False,
- assert_unicode=None,
- unicode_error=None,
- _warn_on_bytestring=False)),
- )
-#
-# New Tables
-#
-# None
-
-#
-# Columns to add to existing tables
-#
-networks_netmask_v6 = Column(
- 'netmask_v6',
- String(length=255, convert_unicode=False, assert_unicode=None,
- unicode_error=None, _warn_on_bytestring=False))
-
-
-def upgrade(migrate_engine):
- # Upgrade operations go here. Don't create your own engine;
- # bind migrate_engine to your metadata
- meta.bind = migrate_engine
-
- # Alter column name
- networks.c.ra_server.alter(name='gateway_v6')
- # Add new column to existing table
- networks.create_column(networks_netmask_v6)
-
- # drop existing columns from table
- fixed_ips.c.addressV6.drop()
- fixed_ips.c.netmaskV6.drop()
- fixed_ips.c.gatewayV6.drop()
+# Copyright 2010 OpenStack LLC.
+# 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.
+
+from sqlalchemy import *
+from migrate import *
+
+from nova import log as logging
+
+
+meta = MetaData()
+
+
+# Table stub-definitions
+# Just for the ForeignKey and column creation to succeed, these are not the
+# actual definitions of instances or services.
+#
+
+#
+# Tables to alter
+#
+networks = Table('networks', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('injected', Boolean(create_constraint=True, name=None)),
+ Column('cidr',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('netmask',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('bridge',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('gateway',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('broadcast',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('dns',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('vlan', Integer()),
+ Column('vpn_public_address',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('vpn_public_port', Integer()),
+ Column('vpn_private_address',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('dhcp_start',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('project_id',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('host',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('cidr_v6',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('ra_server', String(length=255,
+ convert_unicode=False,
+ assert_unicode=None,
+ unicode_error=None,
+ _warn_on_bytestring=False)),
+ Column(
+ 'label',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)))
+
+fixed_ips = Table('fixed_ips', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('address',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('network_id',
+ Integer(),
+ ForeignKey('networks.id'),
+ nullable=True),
+ Column('instance_id',
+ Integer(),
+ ForeignKey('instances.id'),
+ nullable=True),
+ Column('allocated', Boolean(create_constraint=True, name=None)),
+ Column('leased', Boolean(create_constraint=True, name=None)),
+ Column('reserved', Boolean(create_constraint=True, name=None)),
+ Column("addressV6", String(length=255,
+ convert_unicode=False,
+ assert_unicode=None,
+ unicode_error=None,
+ _warn_on_bytestring=False)),
+ Column("netmaskV6", String(length=3,
+ convert_unicode=False,
+ assert_unicode=None,
+ unicode_error=None,
+ _warn_on_bytestring=False)),
+ Column("gatewayV6", String(length=255,
+ convert_unicode=False,
+ assert_unicode=None,
+ unicode_error=None,
+ _warn_on_bytestring=False)),
+ )
+#
+# New Tables
+#
+# None
+
+#
+# Columns to add to existing tables
+#
+networks_netmask_v6 = Column(
+ 'netmask_v6',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False))
+
+
+def upgrade(migrate_engine):
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ # Alter column name
+ networks.c.ra_server.alter(name='gateway_v6')
+ # Add new column to existing table
+ networks.create_column(networks_netmask_v6)
+
+ # drop existing columns from table
+ fixed_ips.c.addressV6.drop()
+ fixed_ips.c.netmaskV6.drop()
+ fixed_ips.c.gatewayV6.drop()
diff --git a/nova/tests/network/__init__.py b/nova/tests/network/__init__.py
index 8f71a30ba..e0d479f8c 100644
--- a/nova/tests/network/__init__.py
+++ b/nova/tests/network/__init__.py
@@ -1,47 +1,47 @@
-import os
-
-from nova import context
-from nova import db
-from nova import flags
-from nova import log as logging
-from nova import utils
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger('nova.tests.network')
-
-
-def binpath(script):
- """Returns the absolute path to a script in bin"""
- return os.path.abspath(os.path.join(__file__, "../../../../bin", script))
-
-
-def lease_ip(private_ip):
- """Run add command on dhcpbridge"""
- network_ref = db.fixed_ip_get_network(context.get_admin_context(),
- private_ip)
- instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
- private_ip)
- cmd = (binpath('nova-dhcpbridge'), 'add',
- instance_ref['mac_address'],
- private_ip, 'fake')
- env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
- 'TESTING': '1',
- 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
- (out, err) = utils.execute(*cmd, addl_env=env)
- LOG.debug("ISSUE_IP: %s, %s ", out, err)
-
-
-def release_ip(private_ip):
- """Run del command on dhcpbridge"""
- network_ref = db.fixed_ip_get_network(context.get_admin_context(),
- private_ip)
- instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
- private_ip)
- cmd = (binpath('nova-dhcpbridge'), 'del',
- instance_ref['mac_address'],
- private_ip, 'fake')
- env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
- 'TESTING': '1',
- 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
- (out, err) = utils.execute(*cmd, addl_env=env)
- LOG.debug("RELEASE_IP: %s, %s ", out, err)
+import os
+
+from nova import context
+from nova import db
+from nova import flags
+from nova import log as logging
+from nova import utils
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.network')
+
+
+def binpath(script):
+ """Returns the absolute path to a script in bin"""
+ return os.path.abspath(os.path.join(__file__, "../../../../bin", script))
+
+
+def lease_ip(private_ip):
+ """Run add command on dhcpbridge"""
+ network_ref = db.fixed_ip_get_network(context.get_admin_context(),
+ private_ip)
+ instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
+ private_ip)
+ cmd = (binpath('nova-dhcpbridge'), 'add',
+ instance_ref['mac_address'],
+ private_ip, 'fake')
+ env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
+ 'TESTING': '1',
+ 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
+ (out, err) = utils.execute(*cmd, addl_env=env)
+ LOG.debug("ISSUE_IP: %s, %s ", out, err)
+
+
+def release_ip(private_ip):
+ """Run del command on dhcpbridge"""
+ network_ref = db.fixed_ip_get_network(context.get_admin_context(),
+ private_ip)
+ instance_ref = db.fixed_ip_get_instance(context.get_admin_context(),
+ private_ip)
+ cmd = (binpath('nova-dhcpbridge'), 'del',
+ instance_ref['mac_address'],
+ private_ip, 'fake')
+ env = {'DNSMASQ_INTERFACE': network_ref['bridge'],
+ 'TESTING': '1',
+ 'FLAGFILE': FLAGS.dhcpbridge_flagfile}
+ (out, err) = utils.execute(*cmd, addl_env=env)
+ LOG.debug("RELEASE_IP: %s, %s ", out, err)
diff --git a/nova/tests/network/base.py b/nova/tests/network/base.py
index 2dd8178ff..988a1de72 100644
--- a/nova/tests/network/base.py
+++ b/nova/tests/network/base.py
@@ -1,154 +1,154 @@
-# 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.
-"""
-Base class of Unit Tests for all network models
-"""
-import IPy
-import os
-
-from nova import context
-from nova import db
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import test
-from nova import utils
-from nova.auth import manager
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger('nova.tests.network')
-
-
-class NetworkTestCase(test.TestCase):
- """Test cases for network code"""
- def setUp(self):
- super(NetworkTestCase, self).setUp()
- # NOTE(vish): if you change these flags, make sure to change the
- # flags in the corresponding section in nova-dhcpbridge
- self.flags(connection_type='fake',
- fake_call=True,
- fake_network=True)
- self.manager = manager.AuthManager()
- self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
- self.projects = []
- self.network = utils.import_object(FLAGS.network_manager)
- self.context = context.RequestContext(project=None, user=self.user)
- for i in range(FLAGS.num_networks):
- name = 'project%s' % i
- project = self.manager.create_project(name, 'netuser', name)
- self.projects.append(project)
- # create the necessary network data for the project
- user_context = context.RequestContext(project=self.projects[i],
- user=self.user)
- host = self.network.get_network_host(user_context.elevated())
- instance_ref = self._create_instance(0)
- self.instance_id = instance_ref['id']
- instance_ref = self._create_instance(1)
- self.instance2_id = instance_ref['id']
-
- def tearDown(self):
- # TODO(termie): this should really be instantiating clean datastores
- # in between runs, one failure kills all the tests
- db.instance_destroy(context.get_admin_context(), self.instance_id)
- db.instance_destroy(context.get_admin_context(), self.instance2_id)
- for project in self.projects:
- self.manager.delete_project(project)
- self.manager.delete_user(self.user)
- super(NetworkTestCase, self).tearDown()
-
- def _create_instance(self, project_num, mac=None):
- if not mac:
- mac = utils.generate_mac()
- project = self.projects[project_num]
- self.context._project = project
- self.context.project_id = project.id
- return db.instance_create(self.context,
- {'project_id': project.id,
- 'mac_address': mac})
-
- def _create_address(self, project_num, instance_id=None):
- """Create an address in given project num"""
- if instance_id is None:
- instance_id = self.instance_id
- self.context._project = self.projects[project_num]
- self.context.project_id = self.projects[project_num].id
- return self.network.allocate_fixed_ip(self.context, instance_id)
-
- def _deallocate_address(self, project_num, address):
- self.context._project = self.projects[project_num]
- self.context.project_id = self.projects[project_num].id
- self.network.deallocate_fixed_ip(self.context, address)
-
- def _is_allocated_in_project(self, address, project_id):
- """Returns true if address is in specified project"""
- project_net = db.network_get_by_bridge(context.get_admin_context(),
- FLAGS.flat_network_bridge)
- network = db.fixed_ip_get_network(context.get_admin_context(),
- address)
- instance = db.fixed_ip_get_instance(context.get_admin_context(),
- address)
- # instance exists until release
- return instance is not None and network['id'] == project_net['id']
-
- def test_private_ipv6(self):
- """Make sure ipv6 is OK"""
- if FLAGS.use_ipv6:
- instance_ref = self._create_instance(0)
- address = self._create_address(0, instance_ref['id'])
- network_ref = db.project_get_network(
- context.get_admin_context(),
- self.context.project_id)
- address_v6 = db.instance_get_fixed_address_v6(
- context.get_admin_context(),
- instance_ref['id'])
- self.assertEqual(instance_ref['mac_address'],
- utils.to_mac(address_v6))
- instance_ref2 = db.fixed_ip_get_instance_v6(
- context.get_admin_context(),
- address_v6)
- self.assertEqual(instance_ref['id'], instance_ref2['id'])
- self.assertEqual(address_v6,
- utils.to_global_ipv6(
- network_ref['cidr_v6'],
- instance_ref['mac_address']))
- self._deallocate_address(0, address)
- db.instance_destroy(context.get_admin_context(),
- instance_ref['id'])
-
- def test_available_ips(self):
- """Make sure the number of available ips for the network is correct
-
- The number of available IP addresses depends on the test
- environment's setup.
-
- Network size is set in test fixture's setUp method.
-
- There are ips reserved at the bottom and top of the range.
- services (network, gateway, CloudPipe, broadcast)
- """
- network = db.project_get_network(context.get_admin_context(),
- self.projects[0].id)
- net_size = flags.FLAGS.network_size
- admin_context = context.get_admin_context()
- total_ips = (db.network_count_available_ips(admin_context,
- network['id']) +
- db.network_count_reserved_ips(admin_context,
- network['id']) +
- db.network_count_allocated_ips(admin_context,
- network['id']))
- self.assertEqual(total_ips, net_size)
+# 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.
+"""
+Base class of Unit Tests for all network models
+"""
+import IPy
+import os
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.auth import manager
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.network')
+
+
+class NetworkTestCase(test.TestCase):
+ """Test cases for network code"""
+ def setUp(self):
+ super(NetworkTestCase, self).setUp()
+ # NOTE(vish): if you change these flags, make sure to change the
+ # flags in the corresponding section in nova-dhcpbridge
+ self.flags(connection_type='fake',
+ fake_call=True,
+ fake_network=True)
+ self.manager = manager.AuthManager()
+ self.user = self.manager.create_user('netuser', 'netuser', 'netuser')
+ self.projects = []
+ self.network = utils.import_object(FLAGS.network_manager)
+ self.context = context.RequestContext(project=None, user=self.user)
+ for i in range(FLAGS.num_networks):
+ name = 'project%s' % i
+ project = self.manager.create_project(name, 'netuser', name)
+ self.projects.append(project)
+ # create the necessary network data for the project
+ user_context = context.RequestContext(project=self.projects[i],
+ user=self.user)
+ host = self.network.get_network_host(user_context.elevated())
+ instance_ref = self._create_instance(0)
+ self.instance_id = instance_ref['id']
+ instance_ref = self._create_instance(1)
+ self.instance2_id = instance_ref['id']
+
+ def tearDown(self):
+ # TODO(termie): this should really be instantiating clean datastores
+ # in between runs, one failure kills all the tests
+ db.instance_destroy(context.get_admin_context(), self.instance_id)
+ db.instance_destroy(context.get_admin_context(), self.instance2_id)
+ for project in self.projects:
+ self.manager.delete_project(project)
+ self.manager.delete_user(self.user)
+ super(NetworkTestCase, self).tearDown()
+
+ def _create_instance(self, project_num, mac=None):
+ if not mac:
+ mac = utils.generate_mac()
+ project = self.projects[project_num]
+ self.context._project = project
+ self.context.project_id = project.id
+ return db.instance_create(self.context,
+ {'project_id': project.id,
+ 'mac_address': mac})
+
+ def _create_address(self, project_num, instance_id=None):
+ """Create an address in given project num"""
+ if instance_id is None:
+ instance_id = self.instance_id
+ self.context._project = self.projects[project_num]
+ self.context.project_id = self.projects[project_num].id
+ return self.network.allocate_fixed_ip(self.context, instance_id)
+
+ def _deallocate_address(self, project_num, address):
+ self.context._project = self.projects[project_num]
+ self.context.project_id = self.projects[project_num].id
+ self.network.deallocate_fixed_ip(self.context, address)
+
+ def _is_allocated_in_project(self, address, project_id):
+ """Returns true if address is in specified project"""
+ project_net = db.network_get_by_bridge(context.get_admin_context(),
+ FLAGS.flat_network_bridge)
+ network = db.fixed_ip_get_network(context.get_admin_context(),
+ address)
+ instance = db.fixed_ip_get_instance(context.get_admin_context(),
+ address)
+ # instance exists until release
+ return instance is not None and network['id'] == project_net['id']
+
+ def test_private_ipv6(self):
+ """Make sure ipv6 is OK"""
+ if FLAGS.use_ipv6:
+ instance_ref = self._create_instance(0)
+ address = self._create_address(0, instance_ref['id'])
+ network_ref = db.project_get_network(
+ context.get_admin_context(),
+ self.context.project_id)
+ address_v6 = db.instance_get_fixed_address_v6(
+ context.get_admin_context(),
+ instance_ref['id'])
+ self.assertEqual(instance_ref['mac_address'],
+ utils.to_mac(address_v6))
+ instance_ref2 = db.fixed_ip_get_instance_v6(
+ context.get_admin_context(),
+ address_v6)
+ self.assertEqual(instance_ref['id'], instance_ref2['id'])
+ self.assertEqual(address_v6,
+ utils.to_global_ipv6(
+ network_ref['cidr_v6'],
+ instance_ref['mac_address']))
+ self._deallocate_address(0, address)
+ db.instance_destroy(context.get_admin_context(),
+ instance_ref['id'])
+
+ def test_available_ips(self):
+ """Make sure the number of available ips for the network is correct
+
+ The number of available IP addresses depends on the test
+ environment's setup.
+
+ Network size is set in test fixture's setUp method.
+
+ There are ips reserved at the bottom and top of the range.
+ services (network, gateway, CloudPipe, broadcast)
+ """
+ network = db.project_get_network(context.get_admin_context(),
+ self.projects[0].id)
+ net_size = flags.FLAGS.network_size
+ admin_context = context.get_admin_context()
+ total_ips = (db.network_count_available_ips(admin_context,
+ network['id']) +
+ db.network_count_reserved_ips(admin_context,
+ network['id']) +
+ db.network_count_allocated_ips(admin_context,
+ network['id']))
+ self.assertEqual(total_ips, net_size)
diff --git a/nova/tests/test_flat_network.py b/nova/tests/test_flat_network.py
index b6f7762c5..dcc617e25 100644
--- a/nova/tests/test_flat_network.py
+++ b/nova/tests/test_flat_network.py
@@ -1,161 +1,161 @@
-# 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.
-"""
-Unit Tests for flat network code
-"""
-import IPy
-import os
-import unittest
-
-from nova import context
-from nova import db
-from nova import exception
-from nova import flags
-from nova import log as logging
-from nova import test
-from nova import utils
-from nova.auth import manager
-from nova.tests.network import base
-
-
-FLAGS = flags.FLAGS
-LOG = logging.getLogger('nova.tests.network')
-
-
-class FlatNetworkTestCase(base.NetworkTestCase):
- """Test cases for network code"""
- def test_public_network_association(self):
- """Makes sure that we can allocate a public ip"""
- # TODO(vish): better way of adding floating ips
-
- self.context._project = self.projects[0]
- self.context.project_id = self.projects[0].id
- pubnet = IPy.IP(flags.FLAGS.floating_range)
- address = str(pubnet[0])
- try:
- db.floating_ip_get_by_address(context.get_admin_context(), address)
- except exception.NotFound:
- db.floating_ip_create(context.get_admin_context(),
- {'address': address,
- 'host': FLAGS.host})
-
- self.assertRaises(NotImplementedError,
- self.network.allocate_floating_ip,
- self.context, self.projects[0].id)
-
- fix_addr = self._create_address(0)
- float_addr = address
- self.assertRaises(NotImplementedError,
- self.network.associate_floating_ip,
- self.context, float_addr, fix_addr)
-
- address = db.instance_get_floating_address(context.get_admin_context(),
- self.instance_id)
- self.assertEqual(address, None)
-
- self.assertRaises(NotImplementedError,
- self.network.disassociate_floating_ip,
- self.context, float_addr)
-
- address = db.instance_get_floating_address(context.get_admin_context(),
- self.instance_id)
- self.assertEqual(address, None)
-
- self.assertRaises(NotImplementedError,
- self.network.deallocate_floating_ip,
- self.context, float_addr)
-
- self.network.deallocate_fixed_ip(self.context, fix_addr)
- db.floating_ip_destroy(context.get_admin_context(), float_addr)
-
- def test_allocate_deallocate_fixed_ip(self):
- """Makes sure that we can allocate and deallocate a fixed ip"""
- address = self._create_address(0)
- self.assertTrue(self._is_allocated_in_project(address,
- self.projects[0].id))
- self._deallocate_address(0, address)
-
- # check if the fixed ip address is really deallocated
- self.assertFalse(self._is_allocated_in_project(address,
- self.projects[0].id))
-
- def test_side_effects(self):
- """Ensures allocating and releasing has no side effects"""
- address = self._create_address(0)
- address2 = self._create_address(1, self.instance2_id)
-
- self.assertTrue(self._is_allocated_in_project(address,
- self.projects[0].id))
- self.assertTrue(self._is_allocated_in_project(address2,
- self.projects[1].id))
-
- self._deallocate_address(0, address)
- self.assertFalse(self._is_allocated_in_project(address,
- self.projects[0].id))
-
- # First address release shouldn't affect the second
- self.assertTrue(self._is_allocated_in_project(address2,
- self.projects[0].id))
-
- self._deallocate_address(1, address2)
- self.assertFalse(self._is_allocated_in_project(address2,
- self.projects[1].id))
-
- def test_ips_are_reused(self):
- """Makes sure that ip addresses that are deallocated get reused"""
- address = self._create_address(0)
- self.network.deallocate_fixed_ip(self.context, address)
-
- address2 = self._create_address(0)
- self.assertEqual(address, address2)
-
- self.network.deallocate_fixed_ip(self.context, address2)
-
- def test_too_many_addresses(self):
- """Test for a NoMoreAddresses exception when all fixed ips are used.
- """
- admin_context = context.get_admin_context()
- network = db.project_get_network(admin_context, self.projects[0].id)
- num_available_ips = db.network_count_available_ips(admin_context,
- network['id'])
- addresses = []
- instance_ids = []
- for i in range(num_available_ips):
- instance_ref = self._create_instance(0)
- instance_ids.append(instance_ref['id'])
- address = self._create_address(0, instance_ref['id'])
- addresses.append(address)
-
- ip_count = db.network_count_available_ips(context.get_admin_context(),
- network['id'])
- self.assertEqual(ip_count, 0)
- self.assertRaises(db.NoMoreAddresses,
- self.network.allocate_fixed_ip,
- self.context,
- 'foo')
-
- for i in range(num_available_ips):
- self.network.deallocate_fixed_ip(self.context, addresses[i])
- db.instance_destroy(context.get_admin_context(), instance_ids[i])
- ip_count = db.network_count_available_ips(context.get_admin_context(),
- network['id'])
- self.assertEqual(ip_count, num_available_ips)
-
- def run(self, result=None):
- if(FLAGS.network_manager == 'nova.network.manager.FlatManager'):
- super(FlatNetworkTestCase, self).run(result)
+# 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.
+"""
+Unit Tests for flat network code
+"""
+import IPy
+import os
+import unittest
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.auth import manager
+from nova.tests.network import base
+
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.network')
+
+
+class FlatNetworkTestCase(base.NetworkTestCase):
+ """Test cases for network code"""
+ def test_public_network_association(self):
+ """Makes sure that we can allocate a public ip"""
+ # TODO(vish): better way of adding floating ips
+
+ self.context._project = self.projects[0]
+ self.context.project_id = self.projects[0].id
+ pubnet = IPy.IP(flags.FLAGS.floating_range)
+ address = str(pubnet[0])
+ try:
+ db.floating_ip_get_by_address(context.get_admin_context(), address)
+ except exception.NotFound:
+ db.floating_ip_create(context.get_admin_context(),
+ {'address': address,
+ 'host': FLAGS.host})
+
+ self.assertRaises(NotImplementedError,
+ self.network.allocate_floating_ip,
+ self.context, self.projects[0].id)
+
+ fix_addr = self._create_address(0)
+ float_addr = address
+ self.assertRaises(NotImplementedError,
+ self.network.associate_floating_ip,
+ self.context, float_addr, fix_addr)
+
+ address = db.instance_get_floating_address(context.get_admin_context(),
+ self.instance_id)
+ self.assertEqual(address, None)
+
+ self.assertRaises(NotImplementedError,
+ self.network.disassociate_floating_ip,
+ self.context, float_addr)
+
+ address = db.instance_get_floating_address(context.get_admin_context(),
+ self.instance_id)
+ self.assertEqual(address, None)
+
+ self.assertRaises(NotImplementedError,
+ self.network.deallocate_floating_ip,
+ self.context, float_addr)
+
+ self.network.deallocate_fixed_ip(self.context, fix_addr)
+ db.floating_ip_destroy(context.get_admin_context(), float_addr)
+
+ def test_allocate_deallocate_fixed_ip(self):
+ """Makes sure that we can allocate and deallocate a fixed ip"""
+ address = self._create_address(0)
+ self.assertTrue(self._is_allocated_in_project(address,
+ self.projects[0].id))
+ self._deallocate_address(0, address)
+
+ # check if the fixed ip address is really deallocated
+ self.assertFalse(self._is_allocated_in_project(address,
+ self.projects[0].id))
+
+ def test_side_effects(self):
+ """Ensures allocating and releasing has no side effects"""
+ address = self._create_address(0)
+ address2 = self._create_address(1, self.instance2_id)
+
+ self.assertTrue(self._is_allocated_in_project(address,
+ self.projects[0].id))
+ self.assertTrue(self._is_allocated_in_project(address2,
+ self.projects[1].id))
+
+ self._deallocate_address(0, address)
+ self.assertFalse(self._is_allocated_in_project(address,
+ self.projects[0].id))
+
+ # First address release shouldn't affect the second
+ self.assertTrue(self._is_allocated_in_project(address2,
+ self.projects[0].id))
+
+ self._deallocate_address(1, address2)
+ self.assertFalse(self._is_allocated_in_project(address2,
+ self.projects[1].id))
+
+ def test_ips_are_reused(self):
+ """Makes sure that ip addresses that are deallocated get reused"""
+ address = self._create_address(0)
+ self.network.deallocate_fixed_ip(self.context, address)
+
+ address2 = self._create_address(0)
+ self.assertEqual(address, address2)
+
+ self.network.deallocate_fixed_ip(self.context, address2)
+
+ def test_too_many_addresses(self):
+ """Test for a NoMoreAddresses exception when all fixed ips are used.
+ """
+ admin_context = context.get_admin_context()
+ network = db.project_get_network(admin_context, self.projects[0].id)
+ num_available_ips = db.network_count_available_ips(admin_context,
+ network['id'])
+ addresses = []
+ instance_ids = []
+ for i in range(num_available_ips):
+ instance_ref = self._create_instance(0)
+ instance_ids.append(instance_ref['id'])
+ address = self._create_address(0, instance_ref['id'])
+ addresses.append(address)
+
+ ip_count = db.network_count_available_ips(context.get_admin_context(),
+ network['id'])
+ self.assertEqual(ip_count, 0)
+ self.assertRaises(db.NoMoreAddresses,
+ self.network.allocate_fixed_ip,
+ self.context,
+ 'foo')
+
+ for i in range(num_available_ips):
+ self.network.deallocate_fixed_ip(self.context, addresses[i])
+ db.instance_destroy(context.get_admin_context(), instance_ids[i])
+ ip_count = db.network_count_available_ips(context.get_admin_context(),
+ network['id'])
+ self.assertEqual(ip_count, num_available_ips)
+
+ def run(self, result=None):
+ if(FLAGS.network_manager == 'nova.network.manager.FlatManager'):
+ super(FlatNetworkTestCase, self).run(result)
--
cgit
From 1aa44c3d8d0a22c5c5bc432d191a15656ad3351d Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Wed, 16 Mar 2011 14:34:44 -0700
Subject: Don't complain about the _ function being used
---
pylintrc | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/pylintrc b/pylintrc
index f07b14980..53cf37719 100644
--- a/pylintrc
+++ b/pylintrc
@@ -1,3 +1,5 @@
+# The format of this file isn't really documented; just use --generate-rcfile
+
[Messages Control]
# W0511: TODOs in code comments are fine.
# W0142: *args and **kwargs are fine.
@@ -25,3 +27,10 @@ no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$
max-public-methods=100
min-public-methods=0
max-args=6
+
+[Variables]
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+# _ is used by our localization
+additional-builtins=_
--
cgit
From a151fabdb7f3edef8ba17b204fe55c73fc15720a Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Wed, 16 Mar 2011 15:25:21 -0700
Subject: In order to disable the messages, we have to use disable, not
disable-msg.
---
pylintrc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pylintrc b/pylintrc
index 53cf37719..378479ec5 100644
--- a/pylintrc
+++ b/pylintrc
@@ -4,7 +4,7 @@
# W0511: TODOs in code comments are fine.
# W0142: *args and **kwargs are fine.
# W0622: Redefining id is fine.
-disable-msg=W0511,W0142,W0622
+disable=W0511,W0142,W0622
[Basic]
# Variable names can be 1 to 31 characters long, with lowercase and underscores
--
cgit
From 7b7033bfb31c610b1f0295e6059ed44931dfe450 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Wed, 16 Mar 2011 15:28:09 -0700
Subject: Don't warn about C0111 (No docstrings)
While docstrings are great, requiring them is probably going too far. Let's get pylint useful first by having it not complain too much, then we can have a second stricter PyLint if desired.
---
pylintrc | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/pylintrc b/pylintrc
index 378479ec5..135eea4d5 100644
--- a/pylintrc
+++ b/pylintrc
@@ -1,10 +1,12 @@
# The format of this file isn't really documented; just use --generate-rcfile
[Messages Control]
+# NOTE(justinsb): We might want to have a 2nd strict pylintrc in future
+# C0111: Don't require docstrings on every method
# W0511: TODOs in code comments are fine.
# W0142: *args and **kwargs are fine.
# W0622: Redefining id is fine.
-disable=W0511,W0142,W0622
+disable=C0111,W0511,W0142,W0622
[Basic]
# Variable names can be 1 to 31 characters long, with lowercase and underscores
--
cgit
From adb9c0f0d933f8a56e688b89cfa632ce5c9e4888 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Wed, 16 Mar 2011 17:48:39 -0500
Subject: commit before monster
---
nova/virt/xenapi/vmops.py | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 27f9a3a17..fbc7ab64d 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -761,10 +761,11 @@ class VMOps(object):
mac_address, device)
device += 1
- def reset_network(self, instance):
+ def reset_network(self, instance, vm_ref):
"""Creates uuid arg to pass to make_agent_call and calls it."""
args = {'id': str(uuid.uuid4())}
- resp = self._make_agent_call('resetnetwork', instance, '', args)
+ resp = self._make_agent_call('resetnetwork', instance, '', args,
+ vm_ref)
def list_from_xenstore(self, vm, path):
"""Runs the xenstore-ls command to get a listing of all records
@@ -805,25 +806,27 @@ class VMOps(object):
"""
self._make_xenstore_call('delete_record', vm, path)
- def _make_xenstore_call(self, method, vm, path, addl_args={}):
+ def _make_xenstore_call(self, method, vm, path, addl_args=None,
+ vm_ref=None):
"""Handles calls to the xenstore xenapi plugin."""
return self._make_plugin_call('xenstore.py', method=method, vm=vm,
- path=path, addl_args=addl_args)
+ path=path, addl_args=addl_args, vm_ref=vm_ref)
- def _make_agent_call(self, method, vm, path, addl_args={}):
+ def _make_agent_call(self, method, vm, path, addl_args=None, vm_ref=None):
"""Abstracts out the interaction with the agent xenapi plugin."""
return self._make_plugin_call('agent', method=method, vm=vm,
- path=path, addl_args=addl_args)
+ path=path, addl_args=addl_args, vm_ref=vm_ref)
- def _make_plugin_call(self, plugin, method, vm, path, addl_args={}):
+ def _make_plugin_call(self, plugin, method, vm, path, addl_args=None,
+ vm_ref=None):
"""Abstracts out the process of calling a method of a xenapi plugin.
Any errors raised by the plugin will in turn raise a RuntimeError here.
"""
instance_id = vm.id
- vm_ref = self._get_vm_opaque_ref(vm)
+ vm_ref = vm_ref or self._get_vm_opaque_ref(vm)
vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
args = {'dom_id': vm_rec['domid'], 'path': path}
- args.update(addl_args)
+ args.update(addl_args or {})
try:
task = self._session.async_call_plugin(plugin, method, args)
ret = self._session.wait_for_task(task, instance_id)
--
cgit
From 038d99d9fa4354bd617adfa332d69a87a9f7918e Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Wed, 16 Mar 2011 18:18:07 -0500
Subject: hacks in place
---
nova/virt/xenapi/vmops.py | 27 ++++++++++++++++-----------
1 file changed, 16 insertions(+), 11 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index fbc7ab64d..a9a6800b1 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -137,7 +137,7 @@ class VMOps(object):
networks = db.network_get_all_by_instance(admin_context,
instance['id'])
network_info = self._get_network_info(instance, networks, IPs)
- self.inject_network_info(vm_ref, network_info)
+ self.inject_network_info(instance, vm_ref, network_info)
self.create_vifs(vm_ref, network_info)
LOG.debug(_('Starting VM %s...'), vm_ref)
@@ -188,7 +188,7 @@ class VMOps(object):
timer.f = _wait_for_boot
# call to reset network to configure network from xenstore
- self.reset_network(instance)
+ self.reset_network(instance, vm_ref)
return timer.start(interval=0.5, now=True)
@@ -724,7 +724,7 @@ class VMOps(object):
network_info.append((network, info))
return network_info
- def inject_network_info(self, vm_ref, network_info):
+ def inject_network_info(self, instance, vm_ref, network_info):
"""
Generate the network info and make calls to place it into the
xenstore and the xenstore param list
@@ -738,7 +738,11 @@ class VMOps(object):
location = 'vm-data/networking/%s' % info['mac'].replace(':', '')
self.write_to_param_xenstore(vm_ref, {location: info})
try:
- self.write_to_xenstore(vm_ref, location, info)
+ # TODO(tr3buchet): fix function call after refactor
+ #self.write_to_xenstore(vm_ref, location, info)
+ self._make_plugin_call('xenstore.py', 'write_record', instance,
+ location, {'value': json.dumps(info)},
+ vm_ref)
except KeyError:
# catch KeyError for domid if instance isn't running
pass
@@ -764,8 +768,10 @@ class VMOps(object):
def reset_network(self, instance, vm_ref):
"""Creates uuid arg to pass to make_agent_call and calls it."""
args = {'id': str(uuid.uuid4())}
- resp = self._make_agent_call('resetnetwork', instance, '', args,
- vm_ref)
+ # TODO(tr3buchet): fix function call after refactor
+ #resp = self._make_agent_call('resetnetwork', instance, '', args)
+ resp = self._make_plugin_call('agent', 'resetnetwork', instance, '',
+ args, vm_ref)
def list_from_xenstore(self, vm, path):
"""Runs the xenstore-ls command to get a listing of all records
@@ -806,16 +812,15 @@ class VMOps(object):
"""
self._make_xenstore_call('delete_record', vm, path)
- def _make_xenstore_call(self, method, vm, path, addl_args=None,
- vm_ref=None):
+ def _make_xenstore_call(self, method, vm, path, addl_args=None):
"""Handles calls to the xenstore xenapi plugin."""
return self._make_plugin_call('xenstore.py', method=method, vm=vm,
- path=path, addl_args=addl_args, vm_ref=vm_ref)
+ path=path, addl_args=addl_args)
- def _make_agent_call(self, method, vm, path, addl_args=None, vm_ref=None):
+ def _make_agent_call(self, method, vm, path, addl_args=None):
"""Abstracts out the interaction with the agent xenapi plugin."""
return self._make_plugin_call('agent', method=method, vm=vm,
- path=path, addl_args=addl_args, vm_ref=vm_ref)
+ path=path, addl_args=addl_args)
def _make_plugin_call(self, plugin, method, vm, path, addl_args=None,
vm_ref=None):
--
cgit
From a766b4111addad804e47b8be3e6dedb5f80a83c4 Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Thu, 17 Mar 2011 02:20:18 +0000
Subject: added in network qos support for xenserver. Pull qos settings from
flavor, use when creating instance.
---
nova/api/openstack/servers.py | 3 ++-
nova/tests/db/fakes.py | 30 ++++++++++++++++++----
nova/tests/test_xenapi.py | 8 ++++++
nova/virt/xenapi/vm_utils.py | 9 ++++---
nova/virt/xenapi/vmops.py | 9 +++++--
plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 2 +-
6 files changed, 49 insertions(+), 12 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 3ecd4fb01..2f26fa873 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -32,6 +32,7 @@ from nova.api.openstack import faults
from nova.auth import manager as auth_manager
from nova.compute import instance_types
from nova.compute import power_state
+from nova.quota import QuotaError
import nova.api.openstack
@@ -189,7 +190,7 @@ class Controller(wsgi.Controller):
metadata=metadata,
injected_files=injected_files)
except QuotaError as error:
- self._handle_quota_error(error)
+ self._handle_quota_errors(error)
server = _translate_keys(instances[0])
password = "%s%s" % (server['server']['name'][:4],
diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py
index 5e9a3aa3b..2d25d5fc5 100644
--- a/nova/tests/db/fakes.py
+++ b/nova/tests/db/fakes.py
@@ -28,13 +28,33 @@ def stub_out_db_instance_api(stubs):
""" Stubs out the db API for creating Instances """
INSTANCE_TYPES = {
- 'm1.tiny': dict(memory_mb=512, vcpus=1, local_gb=0, flavorid=1),
- 'm1.small': dict(memory_mb=2048, vcpus=1, local_gb=20, flavorid=2),
+ 'm1.tiny': dict(memory_mb=512,
+ vcpus=1,
+ local_gb=0,
+ flavorid=1,
+ rxtx_cap=1),
+ 'm1.small': dict(memory_mb=2048,
+ vcpus=1,
+ local_gb=20,
+ flavorid=2,
+ rxtx_cap=2),
'm1.medium':
- dict(memory_mb=4096, vcpus=2, local_gb=40, flavorid=3),
- 'm1.large': dict(memory_mb=8192, vcpus=4, local_gb=80, flavorid=4),
+ dict(memory_mb=4096,
+ vcpus=2,
+ local_gb=40,
+ flavorid=3,
+ rxtx_cap=3),
+ 'm1.large': dict(memory_mb=8192,
+ vcpus=4,
+ local_gb=80,
+ flavorid=4,
+ rxtx_cap=4),
'm1.xlarge':
- dict(memory_mb=16384, vcpus=8, local_gb=160, flavorid=5)}
+ dict(memory_mb=16384,
+ vcpus=8,
+ local_gb=160,
+ flavorid=5,
+ rxtx_cap=5)}
class FakeModel(object):
""" Stubs out for model """
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index 8b0affd5c..66a973a78 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -361,6 +361,14 @@ class XenAPIVMTestCase(test.TestCase):
glance_stubs.FakeGlance.IMAGE_RAMDISK)
self.check_vm_params_for_linux_with_external_kernel()
+ def test_spawn_with_network_qos(self):
+ self._create_instance()
+ for vif_ref in xenapi_fake.get_all('VIF'):
+ vif_rec = xenapi_fake.get_record('VIF', vif_ref)
+ self.assertEquals(vif_rec['qos_algorithm_type'], 'ratelimit')
+ self.assertEquals(vif_rec['qos_algorithm_params']['kbps'],
+ str(4 * 1024))
+
def tearDown(self):
super(XenAPIVMTestCase, self).tearDown()
self.manager.delete_project(self.project)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 763c5fe40..e0621f73a 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -233,7 +233,9 @@ class VMHelper(HelperBase):
raise StorageError(_('Unable to destroy VBD %s') % vbd_ref)
@classmethod
- def create_vif(cls, session, vm_ref, network_ref, mac_address, dev="0"):
+ def create_vif(cls, session, vm_ref, network_ref, mac_address,
+ dev="0",
+ rxtx_cap=0):
"""Create a VIF record. Returns a Deferred that gives the new
VIF reference."""
vif_rec = {}
@@ -243,8 +245,9 @@ class VMHelper(HelperBase):
vif_rec['MAC'] = mac_address
vif_rec['MTU'] = '1500'
vif_rec['other_config'] = {}
- vif_rec['qos_algorithm_type'] = ''
- vif_rec['qos_algorithm_params'] = {}
+ vif_rec['qos_algorithm_type'] = "ratelimit" if rxtx_cap else ''
+ vif_rec['qos_algorithm_params'] = \
+ {"kbps": str(rxtx_cap * 1024)} if rxtx_cap else {}
LOG.debug(_('Creating VIF for VM %(vm_ref)s,'
' network %(network_ref)s.') % locals())
vif_ref = session.call_xenapi('VIF.create', vif_rec)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 488a61e8e..29f162ad1 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -744,8 +744,12 @@ class VMOps(object):
Creates vifs for an instance
"""
- vm_ref = self._get_vm_opaque_ref(instance.id)
+ vm_ref = self._get_vm_opaque_ref(instance['id'])
+ admin_context = context.get_admin_context()
+ flavor = db.instance_type_get_by_name(admin_context,
+ instance.instance_type)
logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
+ rxtx_cap = flavor['rxtx_cap']
if networks is None:
networks = db.network_get_all_by_instance(admin_context,
instance['id'])
@@ -766,7 +770,8 @@ class VMOps(object):
device = "0"
VMHelper.create_vif(self._session, vm_ref, network_ref,
- instance.mac_address, device)
+ instance.mac_address, device,
+ rxtx_cap=rxtx_cap)
def reset_network(self, instance):
"""
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index c996f6ef4..db39cb0f4 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -216,7 +216,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port, os_type):
'x-image-meta-status': 'queued',
'x-image-meta-disk-format': 'vhd',
'x-image-meta-container-format': 'ovf',
- 'x-image-meta-property-os-type': os_type
+ 'x-image-meta-property-os-type': os_type,
}
for header, value in headers.iteritems():
--
cgit
From c85e8fc2d61368b15e4deafb4ae3b723777cf2b0 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 11:37:50 +0100
Subject: Make error message match the check.
---
bin/nova-manage | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index c84891619..b4b75d6b3 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -577,7 +577,7 @@ class VmCommands(object):
if (FLAGS.connection_type != 'libvirt' or
(FLAGS.connection_type == 'libvirt' and
FLAGS.libvirt_type not in ['kvm', 'qemu'])):
- msg = _('Only KVM is supported for now. Sorry!')
+ msg = _('Only KVM and QEmu are supported for now. Sorry!')
raise exception.Error(msg)
if (FLAGS.volume_driver != 'nova.volume.driver.AOEDriver' and \
--
cgit
From 41619f49ce72d8e85f013c5a5dd248faa8490555 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 11:44:27 +0100
Subject: Comparisons to None should not use == or !=.
Stop converting sets to lists before comparing them. They might be in different order after being list()ified.
---
nova/virt/libvirt_conn.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 2559c2b81..0a85da541 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -998,14 +998,14 @@ class LibvirtConnection(object):
topology_node = xml.xpathEval('//host/cpu/topology')[0]\
.get_properties()
topology = dict()
- while topology_node != None:
+ while topology_node:
name = topology_node.get_name()
topology[name] = topology_node.getContent()
topology_node = topology_node.get_next()
keys = ['cores', 'sockets', 'threads']
tkeys = topology.keys()
- if list(set(tkeys)) != list(set(keys)):
+ if set(tkeys) != set(keys):
ks = ', '.join(keys)
raise exception.Invalid(_("Invalid xml: topology(%(topology)s) "
"must have %(ks)s") % locals())
--
cgit
From c5378e09be3d633b79e4a8c62b51d1e56cdaa67b Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 12:58:45 +0100
Subject: Fix a number of place in the volume driver where the argv hadn't been
fully split
---
nova/volume/driver.py | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/nova/volume/driver.py b/nova/volume/driver.py
index 7b4bacdec..9ebc67abc 100644
--- a/nova/volume/driver.py
+++ b/nova/volume/driver.py
@@ -207,8 +207,8 @@ class AOEDriver(VolumeDriver):
(shelf_id,
blade_id) = self.db.volume_get_shelf_and_blade(context,
_volume['id'])
- self._execute("sudo aoe-discover")
- out, err = self._execute("sudo aoe-stat", check_exit_code=False)
+ self._execute('sudo', 'aoe-discover')
+ out, err = self._execute('sudo', 'aoe-stat', check_exit_code=False)
device_path = 'e%(shelf_id)d.%(blade_id)d' % locals()
if out.find(device_path) >= 0:
return "/dev/etherd/%s" % device_path
@@ -224,8 +224,8 @@ class AOEDriver(VolumeDriver):
(shelf_id,
blade_id) = self.db.volume_get_shelf_and_blade(context,
volume_id)
- cmd = "sudo vblade-persist ls --no-header"
- out, _err = self._execute(cmd)
+ cmd = ('sudo', 'vblade-persist', 'ls', '--no-header')
+ out, _err = self._execute(*cmd)
exported = False
for line in out.split('\n'):
param = line.split(' ')
@@ -318,8 +318,8 @@ class ISCSIDriver(VolumeDriver):
iscsi_name = "%s%s" % (FLAGS.iscsi_target_prefix, volume['name'])
volume_path = "/dev/%s/%s" % (FLAGS.volume_group, volume['name'])
self._execute('sudo', 'ietadm', '--op', 'new',
- '--tid=%s --params Name=%s' %
- (iscsi_target, iscsi_name))
+ '--tid=%s' % iscsi_target,
+ '--params', 'Name=%s' % iscsi_name)
self._execute('sudo', 'ietadm', '--op', 'new',
'--tid=%s' % iscsi_target,
'--lun=0', '--params',
@@ -500,7 +500,7 @@ class ISCSIDriver(VolumeDriver):
tid = self.db.volume_get_iscsi_target_num(context, volume_id)
try:
- self._execute("sudo ietadm --op show --tid=%(tid)d" % locals())
+ self._execute('sudo', 'ietadm', '--op', 'show', '--tid=%(tid)d' % locals())
except exception.ProcessExecutionError, e:
# Instances remount read-only in this case.
# /etc/init.d/iscsitarget restart and rebooting nova-volume
@@ -551,7 +551,7 @@ class RBDDriver(VolumeDriver):
def delete_volume(self, volume):
"""Deletes a logical volume."""
self._try_execute('rbd', '--pool', FLAGS.rbd_pool,
- 'rm', voluname['name'])
+ 'rm', volume['name'])
def local_path(self, volume):
"""Returns the path of the rbd volume."""
--
cgit
From 27d5cbaf03e532e30de2b6aacbc330391a0d1735 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 14:51:59 +0100
Subject: Make smoketests' exit code reveal whether they were succesful.
---
smoketests/admin_smoketests.py | 2 +-
smoketests/base.py | 9 +++++++--
smoketests/netadmin_smoketests.py | 2 +-
smoketests/public_network_smoketests.py | 2 +-
smoketests/sysadmin_smoketests.py | 2 +-
5 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/smoketests/admin_smoketests.py b/smoketests/admin_smoketests.py
index 86a7f600d..8d8b4349e 100644
--- a/smoketests/admin_smoketests.py
+++ b/smoketests/admin_smoketests.py
@@ -95,4 +95,4 @@ class UserTests(AdminSmokeTestCase):
if __name__ == "__main__":
suites = {'user': unittest.makeSuite(UserTests)}
- sys.exit(base.run_tests(suites))
+ sys.exit(not base.run_tests(suites))
diff --git a/smoketests/base.py b/smoketests/base.py
index 204b4a1eb..11f67ed6f 100644
--- a/smoketests/base.py
+++ b/smoketests/base.py
@@ -205,7 +205,12 @@ def run_tests(suites):
', '.join(suites.keys())
return 1
- unittest.TextTestRunner(verbosity=2).run(suite)
+ return unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
else:
+ successful = True
for suite in suites.itervalues():
- unittest.TextTestRunner(verbosity=2).run(suite)
+ result = unittest.TextTestRunner(verbosity=2).run(suite)
+ if not result.wasSuccesful():
+ successful = False
+ return successful
+
diff --git a/smoketests/netadmin_smoketests.py b/smoketests/netadmin_smoketests.py
index 38beb8fdc..4aa97c4e2 100644
--- a/smoketests/netadmin_smoketests.py
+++ b/smoketests/netadmin_smoketests.py
@@ -191,4 +191,4 @@ if __name__ == "__main__":
suites = {'address': unittest.makeSuite(AddressTests),
'security_group': unittest.makeSuite(SecurityGroupTests)
}
- sys.exit(base.run_tests(suites))
+ sys.exit(not base.run_tests(suites))
diff --git a/smoketests/public_network_smoketests.py b/smoketests/public_network_smoketests.py
index 5a4c67642..8a2ae3379 100644
--- a/smoketests/public_network_smoketests.py
+++ b/smoketests/public_network_smoketests.py
@@ -184,4 +184,4 @@ class InstanceTestsFromPublic(base.UserSmokeTestCase):
if __name__ == "__main__":
suites = {'instance': unittest.makeSuite(InstanceTestsFromPublic)}
- sys.exit(base.run_tests(suites))
+ sys.exit(not base.run_tests(suites))
diff --git a/smoketests/sysadmin_smoketests.py b/smoketests/sysadmin_smoketests.py
index e3b84d3d3..6648ae7cf 100644
--- a/smoketests/sysadmin_smoketests.py
+++ b/smoketests/sysadmin_smoketests.py
@@ -290,4 +290,4 @@ if __name__ == "__main__":
'instance': unittest.makeSuite(InstanceTests),
'volume': unittest.makeSuite(VolumeTests)
}
- sys.exit(base.run_tests(suites))
+ sys.exit(not base.run_tests(suites))
--
cgit
From 732633c93f8d8cf71875d2caf096c9efbcf9dbce Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: Thu, 17 Mar 2011 09:55:41 -0400
Subject: Update the Openstack API to handle case where personality is set but
null in the request to create a server.
---
nova/api/openstack/servers.py | 5 +++++
nova/tests/api/openstack/test_servers.py | 13 +++++++++++--
2 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 3ecd4fb01..bf21ed17f 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -220,6 +220,11 @@ class Controller(wsgi.Controller):
underlying compute service.
"""
injected_files = []
+
+ # NOTE(dprince): handle case where 'personality: null' is in JSON req
+ if not personality:
+ return injected_files
+
for item in personality:
try:
path = item['path']
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 03e00af2a..230c9d03c 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -943,11 +943,13 @@ class TestServerInstanceCreation(test.TestCase):
server['name'] = 'new-server-test'
server['imageId'] = 1
server['flavorId'] = 1
- if personality_files is not None:
+ if personality_files:
personalities = []
for path, contents in personality_files:
personalities.append({'path': path, 'contents': contents})
server['personality'] = personalities
+ else:
+ server['personality'] = None
return {'server': server}
def _get_create_request_json(self, body_dict):
@@ -976,7 +978,7 @@ class TestServerInstanceCreation(test.TestCase):
for item in metadata.iteritems():
body_parts.append('%s' % item)
body_parts.append('')
- if 'personality' in server:
+ if 'personality' in server and server['personality'] is not None:
personalities = server['personality']
body_parts.append('')
for file in personalities:
@@ -1093,6 +1095,13 @@ class TestServerInstanceCreation(test.TestCase):
self.assertEquals(response.status_int, 400)
self.assertEquals(injected_files, None)
+ def test_create_instance_with_null_personality(self):
+ personality = None
+ request, response, injected_files = \
+ self._create_instance_with_personality_json(personality)
+ self.assertEquals(response.status_int, 200)
+ self.assertEquals(injected_files, [])
+
def test_create_instance_with_three_personalities(self):
files = [
('/etc/sudoers', 'ALL ALL=NOPASSWD: ALL\n'),
--
cgit
From f8aa9485fe2048ff916d9dd40478ef0b1486077f Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: Thu, 17 Mar 2011 10:45:46 -0400
Subject: Switch back to 'is not None' for personality_files check. (makes mark
happy)
---
nova/api/openstack/servers.py | 1 -
nova/tests/api/openstack/test_servers.py | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index bf21ed17f..6dd66a9a5 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -221,7 +221,6 @@ class Controller(wsgi.Controller):
"""
injected_files = []
- # NOTE(dprince): handle case where 'personality: null' is in JSON req
if not personality:
return injected_files
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 230c9d03c..71c57bfbf 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -943,7 +943,7 @@ class TestServerInstanceCreation(test.TestCase):
server['name'] = 'new-server-test'
server['imageId'] = 1
server['flavorId'] = 1
- if personality_files:
+ if personality_files is not None:
personalities = []
for path, contents in personality_files:
personalities.append({'path': path, 'contents': contents})
--
cgit
From 66d9c0d51d410998de86508359135a7d978997ef Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: Thu, 17 Mar 2011 11:05:31 -0400
Subject: Call _create_personality_request_dict within the personalities_null
test.
---
nova/tests/api/openstack/test_servers.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 71c57bfbf..6969e88c7 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -948,8 +948,6 @@ class TestServerInstanceCreation(test.TestCase):
for path, contents in personality_files:
personalities.append({'path': path, 'contents': contents})
server['personality'] = personalities
- else:
- server['personality'] = None
return {'server': server}
def _get_create_request_json(self, body_dict):
@@ -1097,10 +1095,12 @@ class TestServerInstanceCreation(test.TestCase):
def test_create_instance_with_null_personality(self):
personality = None
- request, response, injected_files = \
- self._create_instance_with_personality_json(personality)
+ body_dict = self._create_personality_request_dict(personality)
+ body_dict['server']['personality'] = None
+ request = self._get_create_request_json(body_dict)
+ compute_api, response = \
+ self._run_create_instance_with_mock_compute_api(request)
self.assertEquals(response.status_int, 200)
- self.assertEquals(injected_files, [])
def test_create_instance_with_three_personalities(self):
files = [
--
cgit
From 137bbc37e9fb664d0b97a607b5f69c38df938077 Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: Thu, 17 Mar 2011 11:10:58 -0400
Subject: No need to modify this test case function as well.
---
nova/tests/api/openstack/test_servers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index 6969e88c7..d0b07b7ae 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -976,7 +976,7 @@ class TestServerInstanceCreation(test.TestCase):
for item in metadata.iteritems():
body_parts.append('%s' % item)
body_parts.append('')
- if 'personality' in server and server['personality'] is not None:
+ if 'personality' in server:
personalities = server['personality']
body_parts.append('')
for file in personalities:
--
cgit
From 1d64d0a3d7f25448361ce54e32bba3de68c7afd1 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 16:14:59 +0100
Subject: Remove unconditional raise, probably left over from debugging.
---
nova/api/ec2/cloud.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py
index 40a9da0e7..e257e44e7 100644
--- a/nova/api/ec2/cloud.py
+++ b/nova/api/ec2/cloud.py
@@ -959,7 +959,7 @@ class CloudController(object):
raise exception.NotFound(_('Image %s not found') % image_id)
internal_id = image['id']
del(image['id'])
- raise Exception(image)
+
image['properties']['is_public'] = (operation_type == 'add')
return self.image_service.update(context, internal_id, image)
--
cgit
From ca50fdd2e013a9016b06a9d0263b980a062d5987 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 16:27:08 +0100
Subject: Just use 'if foo' instead of 'if len(foo)'. It will fail as
spectacularly if its not acting on a sequence anyways.
---
nova/virt/libvirt_conn.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 96463d0f2..70a76b897 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -993,20 +993,20 @@ class LibvirtConnection(object):
cpu_info = dict()
arch_nodes = xml.xpathEval('//cpu/arch')
- if len(arch_nodes):
+ if arch_nodes:
cpu_info['arch'] = arch_nodes[0].getContent()
model_nodes = xml.xpathEval('//cpu/model')
- if len(model_nodes):
+ if model_nodes:
cpu_info['model'] = model_nodes[0].getContent()
vendor_nodes = xml.xpathEval('//cpu/vendor')
- if len(vendor_nodes):
+ if vendor_nodes:
cpu_info['vendor'] = vendor_nodes[0].getContent()
topology_nodes = xml.xpathEval('//cpu/topology')
topology = dict()
- if len(topology_nodes):
+ if topology_nodes:
topology_node = topology_nodes[0].get_properties()
while topology_node != None:
name = topology_node.get_name()
--
cgit
From d31e0f0ad048fbd0374170ea76968859a4c6df34 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Thu, 17 Mar 2011 12:39:09 -0400
Subject: Fixed pep8 violation.
---
nova/api/openstack/faults.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py
index d05c61fc7..56f5b8e7e 100644
--- a/nova/api/openstack/faults.py
+++ b/nova/api/openstack/faults.py
@@ -94,5 +94,6 @@ class OverLimitFault(webob.exc.HTTPException):
"""Currently just return the wrapped exception."""
serializer = wsgi.Serializer(self._serialization_metadata)
content_type = request.best_match_content_type()
- self.wrapped_exc.body = serializer.serialize(self.content, content_type)
+ content = serializer.serialize(self.content, content_type)
+ self.wrapped_exc.body = content
return self.wrapped_exc
--
cgit
From f96dea3da633fc71f16de1bdb95e88249b316e29 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Thu, 17 Mar 2011 13:11:40 -0400
Subject: Pep8 error, oddly specific to pep8 v0.5 < x > v0.6
---
nova/api/openstack/faults.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py
index 56f5b8e7e..ccccbd3d2 100644
--- a/nova/api/openstack/faults.py
+++ b/nova/api/openstack/faults.py
@@ -71,9 +71,9 @@ class OverLimitFault(webob.exc.HTTPException):
_serialization_metadata = {
"application/xml": {
"attributes": {
- "overLimitFault": "code"
- }
- }
+ "overLimitFault": "code",
+ },
+ },
}
def __init__(self, message, details, retry_time):
--
cgit
From 6d6d0f686a7f8d47263b7ed725bdae0f322b2a4e Mon Sep 17 00:00:00 2001
From: Ken Pepple
Date: Thu, 17 Mar 2011 11:34:14 -0700
Subject: better implementation of try..except..else
---
nova/image/local.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/image/local.py b/nova/image/local.py
index c304a2212..609d6c42a 100644
--- a/nova/image/local.py
+++ b/nova/image/local.py
@@ -53,13 +53,13 @@ class LocalImageService(service.BaseImageService):
images = []
for image_dir in os.listdir(self._path):
try:
- images.append(int(image_dir, 16))
+ unhexed_image_id = int(image_dir, 16)
except ValueError:
LOG.error(
_("%s is not in correct directory naming format"\
% image_dir))
- except:
- raise
+ else:
+ images.append(unhexed_image_id)
return images
def index(self, context):
--
cgit
From ca267d0e52ed721f1236dc4b6030433fe92d0d51 Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: Thu, 17 Mar 2011 15:27:20 -0400
Subject: Move the check for None personalities into the create method.
---
nova/api/openstack/servers.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 6dd66a9a5..c6422add4 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -172,8 +172,10 @@ class Controller(wsgi.Controller):
for k, v in env['server']['metadata'].items():
metadata.append({'key': k, 'value': v})
- personality = env['server'].get('personality', [])
- injected_files = self._get_injected_files(personality)
+ personality = env['server'].get('personality')
+ injected_files = []
+ if personality:
+ injected_files = self._get_injected_files(personality)
try:
instances = self.compute_api.create(
@@ -221,9 +223,6 @@ class Controller(wsgi.Controller):
"""
injected_files = []
- if not personality:
- return injected_files
-
for item in personality:
try:
path = item['path']
--
cgit
From 192d3ad7bb9cf49abbca98b0d8e9ae822b204365 Mon Sep 17 00:00:00 2001
From: Monsyne Dragon
Date: Thu, 17 Mar 2011 19:34:45 +0000
Subject: fixed code formatting nit.
---
nova/virt/xenapi/vm_utils.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index e0621f73a..7dbca321f 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -234,8 +234,7 @@ class VMHelper(HelperBase):
@classmethod
def create_vif(cls, session, vm_ref, network_ref, mac_address,
- dev="0",
- rxtx_cap=0):
+ dev="0", rxtx_cap=0):
"""Create a VIF record. Returns a Deferred that gives the new
VIF reference."""
vif_rec = {}
--
cgit
From d70bbdf43c4cba5a0b9c0ab93ff06031a2604db6 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 21:03:11 +0100
Subject: I suck at merging.
---
nova/virt/libvirt_conn.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 998615fe9..7d6501406 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1004,7 +1004,7 @@ class LibvirtConnection(object):
if vendor_nodes:
cpu_info['vendor'] = vendor_nodes[0].getContent()
- topology_nodes = xml.xpathEval('//cpu/topology')
+ topology_nodes = xml.xpathEval('//host/cpu/topology')
topology = dict()
if topology_nodes:
topology_node = topology_nodes[0].get_properties()
--
cgit
From c89a477aa97bb4d716180cadd889ff98123625e4 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 21:06:55 +0100
Subject: pep8
---
nova/virt/libvirt_conn.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 7d6501406..ce54af498 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1017,9 +1017,9 @@ class LibvirtConnection(object):
tkeys = topology.keys()
if list(set(tkeys)) != list(set(keys)):
ks = ', '.join(keys)
- raise exception.Invalid(_("Invalid xml: topology(%(topology)s) "
- "must have %(ks)s") % locals())
-
+ raise exception.Invalid(_("Invalid xml: topology"
+ "(%(topology)s) must have "
+ "%(ks)s") % locals())
feature_nodes = xml.xpathEval('//host/cpu/feature')
features = list()
--
cgit
From 3628f50b4ecd2db0377fd9c158248d3b7e8e98ff Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Thu, 17 Mar 2011 16:26:52 -0400
Subject: Better comment for fault. Improved readability of two small sections.
---
nova/api/openstack/faults.py | 5 ++++-
nova/api/openstack/limits.py | 2 +-
nova/tests/api/openstack/test_limits.py | 2 +-
3 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/nova/api/openstack/faults.py b/nova/api/openstack/faults.py
index ccccbd3d2..0e9c4b26f 100644
--- a/nova/api/openstack/faults.py
+++ b/nova/api/openstack/faults.py
@@ -91,7 +91,10 @@ class OverLimitFault(webob.exc.HTTPException):
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, request):
- """Currently just return the wrapped exception."""
+ """
+ Return the wrapped exception with a serialized body conforming to our
+ error format.
+ """
serializer = wsgi.Serializer(self._serialization_metadata)
content_type = request.best_match_content_type()
content = serializer.serialize(self.content, content_type)
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index 1fe519f8a..efc7d193d 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -151,7 +151,7 @@ class Limit(object):
water = self.water_level
val = self.value
- self.remaining = math.floor((cap - water) / cap * val)
+ self.remaining = math.floor(((cap - water) / cap) * val)
self.next_request = now
def _get_time(self):
diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py
index d1db93a59..05cfacc60 100644
--- a/nova/tests/api/openstack/test_limits.py
+++ b/nova/tests/api/openstack/test_limits.py
@@ -273,7 +273,7 @@ class LimiterTest(BaseLimitTestSuite):
def _check_sum(self, num, verb, url, username=None):
"""Check and sum results from checks."""
results = self._check(num, verb, url, username)
- return sum(filter(lambda x: x != None, results))
+ return sum(item for item in results if item)
def test_no_delay_GET(self):
"""
--
cgit
From ccad7a5d36d27a1854d12d3e45d1c6099983e56c Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Thu, 17 Mar 2011 13:29:22 -0700
Subject: Mark instance metadata as deleted when we delete the instance
---
nova/db/sqlalchemy/api.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 44540617f..2bfe9a52a 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -797,6 +797,11 @@ def instance_destroy(context, instance_id):
update({'deleted': 1,
'deleted_at': datetime.datetime.utcnow(),
'updated_at': literal_column('updated_at')})
+ session.query(models.InstanceMetadata).\
+ filter_by(instance_id=instance_id).\
+ update({'deleted': 1,
+ 'deleted_at': datetime.datetime.utcnow(),
+ 'updated_at': literal_column('updated_at')})
@require_context
--
cgit
From b331a3df4d921414409ebb7a738d97e34f782102 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 21:39:55 +0100
Subject: Adjust test cases.
---
nova/tests/test_volume.py | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py
index 1b1d72092..5d68ca2ae 100644
--- a/nova/tests/test_volume.py
+++ b/nova/tests/test_volume.py
@@ -336,8 +336,8 @@ class ISCSITestCase(DriverTestCase):
self.mox.StubOutWithMock(self.volume.driver, '_execute')
for i in volume_id_list:
tid = db.volume_get_iscsi_target_num(self.context, i)
- self.volume.driver._execute("sudo ietadm --op show --tid=%(tid)d"
- % locals())
+ self.volume.driver._execute("sudo", "ietadm", "--op", "show",
+ "--tid=%(tid)d" % locals())
self.stream.truncate(0)
self.mox.ReplayAll()
@@ -355,8 +355,9 @@ class ISCSITestCase(DriverTestCase):
# the first vblade process isn't running
tid = db.volume_get_iscsi_target_num(self.context, volume_id_list[0])
self.mox.StubOutWithMock(self.volume.driver, '_execute')
- self.volume.driver._execute("sudo ietadm --op show --tid=%(tid)d"
- % locals()).AndRaise(exception.ProcessExecutionError())
+ self.volume.driver._execute("sudo", "ietadm", "--op", "show",
+ "--tid=%(tid)d" % locals()
+ ).AndRaise(exception.ProcessExecutionError())
self.mox.ReplayAll()
self.assertRaises(exception.ProcessExecutionError,
--
cgit
From 06c0b81e54adf3fb0635a7cd7679bcdb051e6263 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 21:43:22 +0100
Subject: pep8
---
nova/tests/test_utils.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/nova/tests/test_utils.py b/nova/tests/test_utils.py
index 51cd57c76..e08d229b0 100644
--- a/nova/tests/test_utils.py
+++ b/nova/tests/test_utils.py
@@ -62,7 +62,8 @@ exit 1
'always get passed '
'correctly')
runs = int(runs.strip())
- self.assertEquals(runs, 10, 'Ran %d times instead of 10.' % (runs,))
+ self.assertEquals(runs, 10,
+ 'Ran %d times instead of 10.' % (runs,))
finally:
os.unlink(tmpfilename)
os.unlink(tmpfilename2)
@@ -95,6 +96,7 @@ grep foo
os.unlink(tmpfilename)
os.unlink(tmpfilename2)
+
class GetFromPathTestCase(test.TestCase):
def test_tolerates_nones(self):
f = utils.get_from_path
--
cgit
From a437ea845bd83c8b1da9de81253132cbad6b48b7 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Thu, 17 Mar 2011 16:14:48 -0500
Subject: create vifs before inject network info to remove rxtx_cap from
network info (don't need to inject it)
---
nova/virt/xenapi/vmops.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index a35d36b9e..aff4fb445 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -133,8 +133,8 @@ class VMOps(object):
# create it now. This goes away once nova-multi-nic hits.
if network_info is None:
network_info = self._get_network_info(instance)
- self.inject_network_info(instance, vm_ref, network_info)
self.create_vifs(vm_ref, network_info)
+ self.inject_network_info(instance, vm_ref, network_info)
LOG.debug(_('Starting VM %s...'), vm_ref)
self._start(instance, vm_ref)
--
cgit
From b05bdeaf77ccb91ac59b4a2dde4a6cad94eb22b2 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Thu, 17 Mar 2011 16:17:03 -0500
Subject: syntax error
---
nova/virt/xenapi/vmops.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index aff4fb445..6542630c1 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -698,7 +698,7 @@ class VMOps(object):
networks = db.network_get_all_by_instance(admin_context,
instance['id'])
flavor = db.instance_type_get_by_name(admin_context,
- instance.['instance_type'])
+ instance['instance_type'])
network_info = []
for network in networks:
network_IPs = [ip for ip in IPs if ip.network_id == network.id]
--
cgit
From afda510637577748d311f0779596c6fec17b00fa Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 22:21:55 +0100
Subject: pep8 is hard
---
nova/volume/driver.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/nova/volume/driver.py b/nova/volume/driver.py
index 9ebc67abc..779b46755 100644
--- a/nova/volume/driver.py
+++ b/nova/volume/driver.py
@@ -500,7 +500,8 @@ class ISCSIDriver(VolumeDriver):
tid = self.db.volume_get_iscsi_target_num(context, volume_id)
try:
- self._execute('sudo', 'ietadm', '--op', 'show', '--tid=%(tid)d' % locals())
+ self._execute('sudo', 'ietadm', '--op', 'show',
+ '--tid=%(tid)d' % locals())
except exception.ProcessExecutionError, e:
# Instances remount read-only in this case.
# /etc/init.d/iscsitarget restart and rebooting nova-volume
--
cgit
From cf648ea89015818a3ec7c8d6d59b50f8ed3604f7 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 22:23:35 +0100
Subject: Fix mis-merge
---
nova/virt/libvirt_conn.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 2f6948556..e80b9fbdf 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -1015,7 +1015,7 @@ class LibvirtConnection(object):
keys = ['cores', 'sockets', 'threads']
tkeys = topology.keys()
- if list(set(tkeys)) != list(set(keys)):
+ if set(tkeys) != set(keys):
ks = ', '.join(keys)
raise exception.Invalid(_("Invalid xml: topology"
"(%(topology)s) must have "
--
cgit
From e2d66aaa670817bda9bf1b494b6a4cfde32b6daf Mon Sep 17 00:00:00 2001
From: Anthony Young
Date: Thu, 17 Mar 2011 14:28:03 -0700
Subject: fix for lp712982, and likely a variety of other dashboard error
handling issues. This fix simply causes the default error code for ApiError
to be 'ApiError' rather than 'Unknown', which makes dashboard handle the
error gracefully, and makes euca error output slightly prettier
---
nova/exception.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/exception.py b/nova/exception.py
index 93c5fe3d7..4e2bbdbaf 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -46,7 +46,7 @@ class Error(Exception):
class ApiError(Error):
- def __init__(self, message='Unknown', code='Unknown'):
+ def __init__(self, message='Unknown', code='ApiError'):
self.message = message
self.code = code
super(ApiError, self).__init__('%s: %s' % (code, message))
--
cgit
From 385b513822dff84e2faeb7f1d1b60efdf3d82fab Mon Sep 17 00:00:00 2001
From: Josh Kleinpeter
Date: Thu, 17 Mar 2011 15:09:09 -0700
Subject: Made fixed_range a required parameter for nova-manage network create.
Changed default num_networks to 1; 1000 seems large.
---
bin/nova-manage | 5 +++--
nova/network/manager.py | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index a4d820209..0c39b662c 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -518,11 +518,12 @@ class NetworkCommands(object):
network_size=None, vlan_start=None,
vpn_start=None, fixed_range_v6=None, label='public'):
"""Creates fixed ips for host by range
- arguments: [fixed_range=FLAG], [num_networks=FLAG],
+ arguments: fixed_range=FLAG, [num_networks=FLAG],
[network_size=FLAG], [vlan_start=FLAG],
[vpn_start=FLAG], [fixed_range_v6=FLAG]"""
if not fixed_range:
- fixed_range = FLAGS.fixed_range
+ raise ValueError('Fixed range in the form of 10.0.0.0/8 is '
+ 'required to create networks.')
if not num_networks:
num_networks = FLAGS.num_networks
if not network_size:
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 3dfc48934..f2025af06 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -73,7 +73,7 @@ flags.DEFINE_string('flat_interface', None,
flags.DEFINE_string('flat_network_dhcp_start', '10.0.0.2',
'Dhcp start for FlatDhcp')
flags.DEFINE_integer('vlan_start', 100, 'First VLAN for private networks')
-flags.DEFINE_integer('num_networks', 1000, 'Number of networks to support')
+flags.DEFINE_integer('num_networks', 1, 'Number of networks to support')
flags.DEFINE_string('vpn_ip', '$my_ip',
'Public IP for the cloudpipe VPN servers')
flags.DEFINE_integer('vpn_start', 1000, 'First Vpn port for private networks')
--
cgit
From 57e3d5abb539031b0d5d40e9033aef43de917fef Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 23:37:34 +0100
Subject: Make the smoketests pep8 compliant (they weren't when I started
working on them..)
---
smoketests/base.py | 1 -
smoketests/sysadmin_smoketests.py | 4 ++--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/smoketests/base.py b/smoketests/base.py
index 11f67ed6f..d367d7944 100644
--- a/smoketests/base.py
+++ b/smoketests/base.py
@@ -213,4 +213,3 @@ def run_tests(suites):
if not result.wasSuccesful():
successful = False
return successful
-
diff --git a/smoketests/sysadmin_smoketests.py b/smoketests/sysadmin_smoketests.py
index 6648ae7cf..1e593e963 100644
--- a/smoketests/sysadmin_smoketests.py
+++ b/smoketests/sysadmin_smoketests.py
@@ -34,8 +34,6 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
from smoketests import flags
from smoketests import base
-
-
FLAGS = flags.FLAGS
flags.DEFINE_string('bundle_kernel', 'openwrt-x86-vmlinuz',
'Local kernel file to use for bundling tests')
@@ -46,6 +44,8 @@ TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
TEST_BUCKET = '%s_bucket' % TEST_PREFIX
TEST_KEY = '%s_key' % TEST_PREFIX
TEST_GROUP = '%s_group' % TEST_PREFIX
+
+
class ImageTests(base.UserSmokeTestCase):
def test_001_can_bundle_image(self):
self.assertTrue(self.bundle_image(FLAGS.bundle_image))
--
cgit
From 83523c125af0fcdc740373332bd5a2d4f233dd0e Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Thu, 17 Mar 2011 23:45:35 +0100
Subject: Invert some of the original logic and fix a typo.
---
smoketests/base.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/smoketests/base.py b/smoketests/base.py
index d367d7944..59c7b415c 100644
--- a/smoketests/base.py
+++ b/smoketests/base.py
@@ -203,13 +203,13 @@ def run_tests(suites):
except KeyError:
print >> sys.stderr, 'Available test suites:', \
', '.join(suites.keys())
- return 1
+ return False
return unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
else:
successful = True
for suite in suites.itervalues():
result = unittest.TextTestRunner(verbosity=2).run(suite)
- if not result.wasSuccesful():
+ if not result.wasSuccessful():
successful = False
return successful
--
cgit
From 4940654f04c50c8593f8e5486fa9e4998f2a3fc7 Mon Sep 17 00:00:00 2001
From: Todd Willey
Date: Thu, 17 Mar 2011 19:32:25 -0400
Subject: Changing project manager should make sure that user is a project
member.
---
nova/auth/dbdriver.py | 2 ++
nova/auth/ldapdriver.py | 2 ++
nova/tests/test_auth.py | 7 +++++++
3 files changed, 11 insertions(+)
diff --git a/nova/auth/dbdriver.py b/nova/auth/dbdriver.py
index d8dad8edd..d1e3f2ed5 100644
--- a/nova/auth/dbdriver.py
+++ b/nova/auth/dbdriver.py
@@ -162,6 +162,8 @@ class DbDriver(object):
values['description'] = description
db.project_update(context.get_admin_context(), project_id, values)
+ if not self.is_in_project(manager_uid, project_id):
+ self.add_to_project(manager_uid, project_id)
def add_to_project(self, uid, project_id):
"""Add user to project"""
diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py
index 5da7751a0..647f70db1 100644
--- a/nova/auth/ldapdriver.py
+++ b/nova/auth/ldapdriver.py
@@ -275,6 +275,8 @@ class LdapDriver(object):
attr.append((self.ldap.MOD_REPLACE, 'description', description))
dn = self.__project_to_dn(project_id)
self.conn.modify_s(dn, attr)
+ if not self.is_in_project(manager_uid, project_id):
+ self.add_to_project(manager_uid, project_id)
@sanitize
def add_to_project(self, uid, project_id):
diff --git a/nova/tests/test_auth.py b/nova/tests/test_auth.py
index 2a7817032..885596f56 100644
--- a/nova/tests/test_auth.py
+++ b/nova/tests/test_auth.py
@@ -299,6 +299,13 @@ class AuthManagerTestCase(object):
self.assertEqual('test2', project.project_manager_id)
self.assertEqual('new desc', project.description)
+ def test_modify_project_adds_new_manager(self):
+ with user_and_project_generator(self.manager):
+ with user_generator(self.manager, name='test2'):
+ self.manager.modify_project('testproj', 'test2', 'new desc')
+ project = self.manager.get_project('testproj')
+ self.assertTrue('test2' in project.member_ids)
+
def test_can_delete_project(self):
with user_generator(self.manager):
self.manager.create_project('testproj', 'test1')
--
cgit
From 79ed4a643df34029391685e13f04e3bfb8afa215 Mon Sep 17 00:00:00 2001
From: Todd Willey
Date: Thu, 17 Mar 2011 19:41:16 -0400
Subject: Add topic name to cast/call logs.
---
nova/rpc.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/rpc.py b/nova/rpc.py
index fbb90299b..58715963a 100644
--- a/nova/rpc.py
+++ b/nova/rpc.py
@@ -311,7 +311,7 @@ def _pack_context(msg, context):
def call(context, topic, msg):
"""Sends a message on a topic and wait for a response"""
- LOG.debug(_("Making asynchronous call..."))
+ LOG.debug(_("Making asynchronous call on %s ..."), topic)
msg_id = uuid.uuid4().hex
msg.update({'_msg_id': msg_id})
LOG.debug(_("MSG_ID is %s") % (msg_id))
@@ -352,7 +352,7 @@ def call(context, topic, msg):
def cast(context, topic, msg):
"""Sends a message on a topic without waiting for a response"""
- LOG.debug(_("Making asynchronous cast..."))
+ LOG.debug(_("Making asynchronous cast on %s..."), topic)
_pack_context(msg, context)
conn = Connection.instance()
publisher = TopicPublisher(connection=conn, topic=topic)
--
cgit
From 59d2a315b87fad6d88a31994546d99d859f1849b Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Thu, 17 Mar 2011 18:26:20 -0700
Subject: Fix for LP Bug #737240
---
contrib/boto_v6/ec2/connection.py | 98 +++++++++++++++++++++++++++++++++++++++
smoketests/sysadmin_smoketests.py | 12 ++---
2 files changed, 104 insertions(+), 6 deletions(-)
diff --git a/contrib/boto_v6/ec2/connection.py b/contrib/boto_v6/ec2/connection.py
index 23466e5d7..faecae95e 100644
--- a/contrib/boto_v6/ec2/connection.py
+++ b/contrib/boto_v6/ec2/connection.py
@@ -39,3 +39,101 @@ class EC2ConnectionV6(boto.ec2.EC2Connection):
self.build_filter_params(params, filters)
return self.get_list('DescribeInstancesV6', params,
[('item', ReservationV6)])
+
+ def run_instances(self, image_id, min_count=1, max_count=1,
+ key_name=None, security_groups=None,
+ user_data=None, addressing_type=None,
+ instance_type='m1.small', placement=None,
+ kernel_id=None, ramdisk_id=None,
+ monitoring_enabled=False, subnet_id=None,
+ block_device_map=None):
+ """
+ Runs an image on EC2.
+
+ :type image_id: string
+ :param image_id: The ID of the image to run
+
+ :type min_count: int
+ :param min_count: The minimum number of instances to launch
+
+ :type max_count: int
+ :param max_count: The maximum number of instances to launch
+
+ :type key_name: string
+ :param key_name: The name of the key pair with which to
+ launch instances
+
+ :type security_groups: list of strings
+ :param security_groups: The names of the security groups with
+ which to associate instances
+
+ :type user_data: string
+ :param user_data: The user data passed to the launched instances
+
+ :type instance_type: string
+ :param instance_type: The type of instance to run
+ (m1.small, m1.large, m1.xlarge)
+
+ :type placement: string
+ :param placement: The availability zone in which to launch
+ the instances
+
+ :type kernel_id: string
+ :param kernel_id: The ID of the kernel with which to
+ launch the instances
+
+ :type ramdisk_id: string
+ :param ramdisk_id: The ID of the RAM disk with which to
+ launch the instances
+
+ :type monitoring_enabled: bool
+ :param monitoring_enabled: Enable CloudWatch monitoring
+ on the instance.
+
+ :type subnet_id: string
+ :param subnet_id: The subnet ID within which to launch
+ the instances for VPC.
+
+ :type block_device_map:
+ :class:`boto.ec2.blockdevicemapping.BlockDeviceMapping`
+ :param block_device_map: A BlockDeviceMapping data structure
+ describing the EBS volumes associated
+ with the Image.
+
+ :rtype: Reservation
+ :return: The :class:`boto.ec2.instance.Reservation`
+ associated with the request for machines
+ """
+ params = {'ImageId': image_id,
+ 'MinCount': min_count,
+ 'MaxCount': max_count}
+ if key_name:
+ params['KeyName'] = key_name
+ if security_groups:
+ l = []
+ for group in security_groups:
+ if isinstance(group, SecurityGroup):
+ l.append(group.name)
+ else:
+ l.append(group)
+ self.build_list_params(params, l, 'SecurityGroup')
+ if user_data:
+ params['UserData'] = base64.b64encode(user_data)
+ if addressing_type:
+ params['AddressingType'] = addressing_type
+ if instance_type:
+ params['InstanceType'] = instance_type
+ if placement:
+ params['Placement.AvailabilityZone'] = placement
+ if kernel_id:
+ params['KernelId'] = kernel_id
+ if ramdisk_id:
+ params['RamdiskId'] = ramdisk_id
+ if monitoring_enabled:
+ params['Monitoring.Enabled'] = 'true'
+ if subnet_id:
+ params['SubnetId'] = subnet_id
+ if block_device_map:
+ block_device_map.build_list_params(params)
+ return self.get_object('RunInstances', params,
+ ReservationV6, verb='POST')
diff --git a/smoketests/sysadmin_smoketests.py b/smoketests/sysadmin_smoketests.py
index e3b84d3d3..e92cc1881 100644
--- a/smoketests/sysadmin_smoketests.py
+++ b/smoketests/sysadmin_smoketests.py
@@ -34,8 +34,6 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
from smoketests import flags
from smoketests import base
-
-
FLAGS = flags.FLAGS
flags.DEFINE_string('bundle_kernel', 'openwrt-x86-vmlinuz',
'Local kernel file to use for bundling tests')
@@ -46,6 +44,8 @@ TEST_PREFIX = 'test%s' % int(random.random() * 1000000)
TEST_BUCKET = '%s_bucket' % TEST_PREFIX
TEST_KEY = '%s_key' % TEST_PREFIX
TEST_GROUP = '%s_group' % TEST_PREFIX
+
+
class ImageTests(base.UserSmokeTestCase):
def test_001_can_bundle_image(self):
self.assertTrue(self.bundle_image(FLAGS.bundle_image))
@@ -148,7 +148,8 @@ class InstanceTests(base.UserSmokeTestCase):
self.fail('could not ping instance')
if FLAGS.use_ipv6:
- if not self.wait_for_ping(self.data['instance'].ip_v6, "ping6"):
+ if not self.wait_for_ping(self.data['instance'].dns_name_v6,
+ "ping6"):
self.fail('could not ping instance v6')
def test_005_can_ssh_to_private_ip(self):
@@ -157,7 +158,7 @@ class InstanceTests(base.UserSmokeTestCase):
self.fail('could not ssh to instance')
if FLAGS.use_ipv6:
- if not self.wait_for_ssh(self.data['instance'].ip_v6,
+ if not self.wait_for_ssh(self.data['instance'].dns_name_v6,
TEST_KEY):
self.fail('could not ssh to instance v6')
@@ -286,8 +287,7 @@ class VolumeTests(base.UserSmokeTestCase):
if __name__ == "__main__":
- suites = {'image': unittest.makeSuite(ImageTests),
+ suites = {
'instance': unittest.makeSuite(InstanceTests),
- 'volume': unittest.makeSuite(VolumeTests)
}
sys.exit(base.run_tests(suites))
--
cgit
From 51ed0ccf8d841443561a476a03c3446844e1a0a8 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Thu, 17 Mar 2011 18:54:51 -0700
Subject: Fix for LP Bug #737240
---
smoketests/sysadmin_smoketests.py | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/smoketests/sysadmin_smoketests.py b/smoketests/sysadmin_smoketests.py
index e92cc1881..2bfc1ac88 100644
--- a/smoketests/sysadmin_smoketests.py
+++ b/smoketests/sysadmin_smoketests.py
@@ -285,9 +285,8 @@ class VolumeTests(base.UserSmokeTestCase):
self.conn.terminate_instances([self.data['instance'].id])
self.conn.delete_key_pair(TEST_KEY)
-
if __name__ == "__main__":
- suites = {
+ suites = {'image': unittest.makeSuite(ImageTests),
'instance': unittest.makeSuite(InstanceTests),
- }
+ 'volume': unittest.makeSuite(VolumeTests)}
sys.exit(base.run_tests(suites))
--
cgit
From 9608ef7d49dd5181f45bd458cea676f79116c39f Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 18 Mar 2011 11:06:58 +0100
Subject: Query the size of the block device, not the size of the filesystem.
---
smoketests/sysadmin_smoketests.py | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/smoketests/sysadmin_smoketests.py b/smoketests/sysadmin_smoketests.py
index 1e593e963..3adb1e0f0 100644
--- a/smoketests/sysadmin_smoketests.py
+++ b/smoketests/sysadmin_smoketests.py
@@ -255,12 +255,13 @@ class VolumeTests(base.UserSmokeTestCase):
ip = self.data['instance'].private_dns_name
conn = self.connect_ssh(ip, TEST_KEY)
stdin, stdout, stderr = conn.exec_command(
- "df -h | grep %s | awk {'print $2'}" % self.device)
- out = stdout.read()
+ "blockdev --getsize64 %s" % self.device)
+ out = stdout.read().strip()
conn.close()
- if not out.strip() == '1007.9M':
- self.fail('Volume is not the right size: %s %s' %
- (out, stderr.read()))
+ expected_size = 1024*1024*1024
+ self.assertEquals('%s' % (expected_size,), out,
+ 'Volume is not the right size: %s %s. Expected: %s' %
+ (out, stderr.read(), expected_size))
def test_006_me_can_umount_volume(self):
ip = self.data['instance'].private_dns_name
--
cgit
From 9eb64af0d7182f19fd7eda75371e202022f79891 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 18 Mar 2011 11:27:38 +0100
Subject: Make proxy.sh work with both openbsd and traditional variants of
netcat.
---
smoketests/proxy.sh | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/smoketests/proxy.sh b/smoketests/proxy.sh
index 9b3f3108a..b9057fe9d 100755
--- a/smoketests/proxy.sh
+++ b/smoketests/proxy.sh
@@ -11,12 +11,19 @@
mkfifo backpipe1
mkfifo backpipe2
+if nc -h 2>&1 | grep -i openbsd
+then
+ NC_LISTEN="nc -l"
+else
+ NC_LISTEN="nc -l -p"
+fi
+
# NOTE(vish): proxy metadata on port 80
while true; do
- nc -l -p 80 0backpipe1
+ $NC_LISTEN 80 0backpipe1
done &
# NOTE(vish): proxy google on port 8080
while true; do
- nc -l -p 8080 0backpipe2
+ $NC_LISTEN 8080 0backpipe2
done &
--
cgit
From 6e632e9ef2907f0b00d3026379af03abe5024bc7 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 18 Mar 2011 13:14:37 +0100
Subject: Make flag parsing work again.
---
smoketests/run_tests.py | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/smoketests/run_tests.py b/smoketests/run_tests.py
index 4f06f0f2b..e7f6eced7 100644
--- a/smoketests/run_tests.py
+++ b/smoketests/run_tests.py
@@ -60,12 +60,23 @@ import os
import unittest
import sys
+# 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 nose import config
from nose import core
from nose import result
+from smoketests import flags
+FLAGS = flags.FLAGS
class _AnsiColorizer(object):
"""
@@ -284,6 +295,7 @@ if __name__ == '__main__':
'running this test.')
sys.exit(1)
+ argv = FLAGS(sys.argv)
testdir = os.path.abspath("./")
c = config.Config(stream=sys.stdout,
env=os.environ,
@@ -294,4 +306,4 @@ if __name__ == '__main__':
runner = NovaTestRunner(stream=c.stream,
verbosity=c.verbosity,
config=c)
- sys.exit(not core.run(config=c, testRunner=runner, argv=sys.argv))
+ sys.exit(not core.run(config=c, testRunner=runner, argv=argv))
--
cgit
From 4b18488223d2c51958855456cb4f5877f331aaa1 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Fri, 18 Mar 2011 13:17:40 +0100
Subject: PEP-8
---
smoketests/base.py | 1 -
smoketests/run_tests.py | 1 +
smoketests/test_sysadmin.py | 4 ++--
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/smoketests/base.py b/smoketests/base.py
index d32d4766c..3e2446c9a 100644
--- a/smoketests/base.py
+++ b/smoketests/base.py
@@ -190,4 +190,3 @@ class UserSmokeTestCase(SmokeTestCase):
global TEST_DATA
self.conn = self.connection_for_env()
self.data = TEST_DATA
-
diff --git a/smoketests/run_tests.py b/smoketests/run_tests.py
index e7f6eced7..62bdfbec6 100644
--- a/smoketests/run_tests.py
+++ b/smoketests/run_tests.py
@@ -78,6 +78,7 @@ from nose import result
from smoketests import flags
FLAGS = flags.FLAGS
+
class _AnsiColorizer(object):
"""
A colorizer is an object that loosely wraps around a stream, allowing
diff --git a/smoketests/test_sysadmin.py b/smoketests/test_sysadmin.py
index 40339869d..15c3b9d57 100644
--- a/smoketests/test_sysadmin.py
+++ b/smoketests/test_sysadmin.py
@@ -265,10 +265,10 @@ class VolumeTests(base.UserSmokeTestCase):
ip = self.data['instance'].private_dns_name
conn = self.connect_ssh(ip, TEST_KEY)
stdin, stdout, stderr = conn.exec_command(
- "blockdev --getsize64 %s" % self.device)
+ "blockdev --getsize64 %s" % self.device)
out = stdout.read().strip()
conn.close()
- expected_size = 1024*1024*1024
+ expected_size = 1024 * 1024 * 1024
self.assertEquals('%s' % (expected_size,), out,
'Volume is not the right size: %s %s. Expected: %s' %
(out, stderr.read(), expected_size))
--
cgit
From 6e9a95fe81c389c672b5150d64749b274975f7bc Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Fri, 18 Mar 2011 09:56:05 -0400
Subject: disable-msg -> disable
---
bin/nova-ajax-console-proxy | 2 +-
bin/nova-api | 2 +-
bin/nova-direct-api | 2 +-
bin/nova-instancemonitor | 2 +-
bin/nova-objectstore | 2 +-
nova/auth/fakeldap.py | 8 ++++----
nova/auth/ldapdriver.py | 2 +-
nova/auth/manager.py | 2 +-
nova/compute/manager.py | 4 ++--
nova/db/api.py | 2 +-
nova/db/base.py | 2 +-
nova/db/sqlalchemy/api.py | 2 +-
nova/network/linux_net.py | 6 +++---
nova/network/manager.py | 4 ++--
nova/objectstore/handler.py | 14 +++++++-------
nova/rpc.py | 6 +++---
nova/service.py | 2 +-
nova/tests/api/test_wsgi.py | 2 +-
nova/tests/hyperv_unittest.py | 2 +-
nova/tests/objectstore_unittest.py | 8 ++++----
nova/tests/test_api.py | 2 +-
nova/tests/test_middleware.py | 4 ++--
po/nova.pot | 10 +++++-----
tools/euca-get-ajax-console | 2 +-
24 files changed, 47 insertions(+), 47 deletions(-)
diff --git a/bin/nova-ajax-console-proxy b/bin/nova-ajax-console-proxy
index bbd60bade..b4ba157e1 100755
--- a/bin/nova-ajax-console-proxy
+++ b/bin/nova-ajax-console-proxy
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
diff --git a/bin/nova-api b/bin/nova-api
index 06bb855cb..a1088c23d 100755
--- a/bin/nova-api
+++ b/bin/nova-api
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
diff --git a/bin/nova-direct-api b/bin/nova-direct-api
index bf29d9a5e..a2c9f1557 100755
--- a/bin/nova-direct-api
+++ b/bin/nova-direct-api
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
diff --git a/bin/nova-instancemonitor b/bin/nova-instancemonitor
index 24cc9fd23..b9d4e49d7 100755
--- a/bin/nova-instancemonitor
+++ b/bin/nova-instancemonitor
@@ -50,7 +50,7 @@ if __name__ == '__main__':
if __name__ == '__builtin__':
LOG.warn(_('Starting instance monitor'))
- # pylint: disable-msg=C0103
+ # pylint: disable=C0103
monitor = monitor.InstanceMonitor()
# This is the parent service that twistd will be looking for when it
diff --git a/bin/nova-objectstore b/bin/nova-objectstore
index 9fbe228a2..94ef2a8d5 100755
--- a/bin/nova-objectstore
+++ b/bin/nova-objectstore
@@ -49,4 +49,4 @@ if __name__ == '__main__':
twistd.serve(__file__)
if __name__ == '__builtin__':
- application = handler.get_application() # pylint: disable-msg=C0103
+ application = handler.get_application() # pylint: disable=C0103
diff --git a/nova/auth/fakeldap.py b/nova/auth/fakeldap.py
index 4466051f0..e8f5771d5 100644
--- a/nova/auth/fakeldap.py
+++ b/nova/auth/fakeldap.py
@@ -90,12 +90,12 @@ MOD_DELETE = 1
MOD_REPLACE = 2
-class NO_SUCH_OBJECT(Exception): # pylint: disable-msg=C0103
+class NO_SUCH_OBJECT(Exception): # pylint: disable=C0103
"""Duplicate exception class from real LDAP module."""
pass
-class OBJECT_CLASS_VIOLATION(Exception): # pylint: disable-msg=C0103
+class OBJECT_CLASS_VIOLATION(Exception): # pylint: disable=C0103
"""Duplicate exception class from real LDAP module."""
pass
@@ -268,7 +268,7 @@ class FakeLDAP(object):
# get the attributes from the store
attrs = store.hgetall(key)
# turn the values from the store into lists
- # pylint: disable-msg=E1103
+ # pylint: disable=E1103
attrs = dict([(k, _from_json(v))
for k, v in attrs.iteritems()])
# filter the objects by query
@@ -283,6 +283,6 @@ class FakeLDAP(object):
return objects
@property
- def __prefix(self): # pylint: disable-msg=R0201
+ def __prefix(self): # pylint: disable=R0201
"""Get the prefix to use for all keys."""
return 'ldap:'
diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py
index 647f70db1..fcac55510 100644
--- a/nova/auth/ldapdriver.py
+++ b/nova/auth/ldapdriver.py
@@ -634,6 +634,6 @@ class LdapDriver(object):
class FakeLdapDriver(LdapDriver):
"""Fake Ldap Auth driver"""
- def __init__(self): # pylint: disable-msg=W0231
+ def __init__(self): # pylint: disable=W0231
__import__('nova.auth.fakeldap')
self.ldap = sys.modules['nova.auth.fakeldap']
diff --git a/nova/auth/manager.py b/nova/auth/manager.py
index 450ab803a..29811ea16 100644
--- a/nova/auth/manager.py
+++ b/nova/auth/manager.py
@@ -22,7 +22,7 @@ Nova authentication management
import os
import shutil
-import string # pylint: disable-msg=W0402
+import string # pylint: disable=W0402
import tempfile
import uuid
import zipfile
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 92deca813..c2781f6fb 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -220,7 +220,7 @@ class ComputeManager(manager.Manager):
self.db.instance_update(context,
instance_id,
{'launched_at': now})
- except Exception: # pylint: disable-msg=W0702
+ except Exception: # pylint: disable=W0702
LOG.exception(_("instance %s: Failed to spawn"), instance_id,
context=context)
self.db.instance_set_state(context,
@@ -692,7 +692,7 @@ class ComputeManager(manager.Manager):
volume_id,
instance_id,
mountpoint)
- except Exception as exc: # pylint: disable-msg=W0702
+ except Exception as exc: # pylint: disable=W0702
# NOTE(vish): The inline callback eats the exception info so we
# log the traceback here and reraise the same
# ecxception below.
diff --git a/nova/db/api.py b/nova/db/api.py
index 3cb0e5811..94777f413 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -608,7 +608,7 @@ def network_get_all(context):
return IMPL.network_get_all(context)
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
def network_get_associated_fixed_ips(context, network_id):
"""Get all network's ips that have been associated."""
return IMPL.network_get_associated_fixed_ips(context, network_id)
diff --git a/nova/db/base.py b/nova/db/base.py
index 1d1e80866..a0f2180c6 100644
--- a/nova/db/base.py
+++ b/nova/db/base.py
@@ -33,4 +33,4 @@ class Base(object):
def __init__(self, db_driver=None):
if not db_driver:
db_driver = FLAGS.db_driver
- self.db = utils.import_object(db_driver) # pylint: disable-msg=C0103
+ self.db = utils.import_object(db_driver) # pylint: disable=C0103
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 9d9b86c1d..394d9a90a 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1249,7 +1249,7 @@ def network_get_all(context):
# NOTE(vish): pylint complains because of the long method name, but
# it fits with the names of the rest of the methods
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
@require_admin_context
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 7106e6164..565732869 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -582,7 +582,7 @@ def update_dhcp(context, network_id):
try:
_execute('sudo', 'kill', '-HUP', pid)
return
- except Exception as exc: # pylint: disable-msg=W0703
+ except Exception as exc: # pylint: disable=W0703
LOG.debug(_("Hupping dnsmasq threw %s"), exc)
else:
LOG.debug(_("Pid %d is stale, relaunching dnsmasq"), pid)
@@ -626,7 +626,7 @@ interface %s
if conffile in out:
try:
_execute('sudo', 'kill', pid)
- except Exception as exc: # pylint: disable-msg=W0703
+ except Exception as exc: # pylint: disable=W0703
LOG.debug(_("killing radvd threw %s"), exc)
else:
LOG.debug(_("Pid %d is stale, relaunching radvd"), pid)
@@ -713,7 +713,7 @@ def _stop_dnsmasq(network):
if pid:
try:
_execute('sudo', 'kill', '-TERM', pid)
- except Exception as exc: # pylint: disable-msg=W0703
+ except Exception as exc: # pylint: disable=W0703
LOG.debug(_("Killing dnsmasq threw %s"), exc)
diff --git a/nova/network/manager.py b/nova/network/manager.py
index 3dfc48934..0decb126a 100644
--- a/nova/network/manager.py
+++ b/nova/network/manager.py
@@ -322,12 +322,12 @@ class NetworkManager(manager.Manager):
self._create_fixed_ips(context, network_ref['id'])
@property
- def _bottom_reserved_ips(self): # pylint: disable-msg=R0201
+ def _bottom_reserved_ips(self): # pylint: disable=R0201
"""Number of reserved ips at the bottom of the range."""
return 2 # network, gateway
@property
- def _top_reserved_ips(self): # pylint: disable-msg=R0201
+ def _top_reserved_ips(self): # pylint: disable=R0201
"""Number of reserved ips at the top of the range."""
return 1 # broadcast
diff --git a/nova/objectstore/handler.py b/nova/objectstore/handler.py
index 05ddace4b..554c72848 100644
--- a/nova/objectstore/handler.py
+++ b/nova/objectstore/handler.py
@@ -167,7 +167,7 @@ class S3(ErrorHandlingResource):
def __init__(self):
ErrorHandlingResource.__init__(self)
- def getChild(self, name, request): # pylint: disable-msg=C0103
+ def getChild(self, name, request): # pylint: disable=C0103
"""Returns either the image or bucket resource"""
request.context = get_context(request)
if name == '':
@@ -177,7 +177,7 @@ class S3(ErrorHandlingResource):
else:
return BucketResource(name)
- def render_GET(self, request): # pylint: disable-msg=R0201
+ def render_GET(self, request): # pylint: disable=R0201
"""Renders the GET request for a list of buckets as XML"""
LOG.debug(_('List of buckets requested'), context=request.context)
buckets = [b for b in bucket.Bucket.all()
@@ -355,7 +355,7 @@ class ImagesResource(resource.Resource):
else:
return ImageResource(name)
- def render_GET(self, request): # pylint: disable-msg=R0201
+ def render_GET(self, request): # pylint: disable=R0201
""" returns a json listing of all images
that a user has permissions to see """
@@ -384,7 +384,7 @@ class ImagesResource(resource.Resource):
request.finish()
return server.NOT_DONE_YET
- def render_PUT(self, request): # pylint: disable-msg=R0201
+ def render_PUT(self, request): # pylint: disable=R0201
""" create a new registered image """
image_id = get_argument(request, 'image_id', u'')
@@ -413,7 +413,7 @@ class ImagesResource(resource.Resource):
p.start()
return ''
- def render_POST(self, request): # pylint: disable-msg=R0201
+ def render_POST(self, request): # pylint: disable=R0201
"""Update image attributes: public/private"""
# image_id required for all requests
@@ -441,7 +441,7 @@ class ImagesResource(resource.Resource):
image_object.update_user_editable_fields(clean_args)
return ''
- def render_DELETE(self, request): # pylint: disable-msg=R0201
+ def render_DELETE(self, request): # pylint: disable=R0201
"""Delete a registered image"""
image_id = get_argument(request, "image_id", u"")
image_object = image.Image(image_id)
@@ -471,7 +471,7 @@ def get_application():
application = service.Application("objectstore")
# Disabled because of lack of proper introspection in Twisted
# or possibly different versions of twisted?
- # pylint: disable-msg=E1101
+ # pylint: disable=E1101
objectStoreService = internet.TCPServer(FLAGS.s3_port, factory,
interface=FLAGS.s3_listen_host)
objectStoreService.setServiceParent(application)
diff --git a/nova/rpc.py b/nova/rpc.py
index 58715963a..5935e1fb3 100644
--- a/nova/rpc.py
+++ b/nova/rpc.py
@@ -62,7 +62,7 @@ class Connection(carrot_connection.BrokerConnection):
params['backend_cls'] = fakerabbit.Backend
# NOTE(vish): magic is fun!
- # pylint: disable-msg=W0142
+ # pylint: disable=W0142
if new:
return cls(**params)
else:
@@ -114,7 +114,7 @@ class Consumer(messaging.Consumer):
if self.failed_connection:
# NOTE(vish): connection is defined in the parent class, we can
# recreate it as long as we create the backend too
- # pylint: disable-msg=W0201
+ # pylint: disable=W0201
self.connection = Connection.recreate()
self.backend = self.connection.create_backend()
self.declare()
@@ -125,7 +125,7 @@ class Consumer(messaging.Consumer):
# NOTE(vish): This is catching all errors because we really don't
# want exceptions to be logged 10 times a second if some
# persistent failure occurs.
- except Exception: # pylint: disable-msg=W0703
+ except Exception: # pylint: disable=W0703
if not self.failed_connection:
LOG.exception(_("Failed to fetch message from queue"))
self.failed_connection = True
diff --git a/nova/service.py b/nova/service.py
index d60df987c..52bb15ad7 100644
--- a/nova/service.py
+++ b/nova/service.py
@@ -217,7 +217,7 @@ class Service(object):
logging.error(_("Recovered model server connection!"))
# TODO(vish): this should probably only catch connection errors
- except Exception: # pylint: disable-msg=W0702
+ except Exception: # pylint: disable=W0702
if not getattr(self, "model_disconnected", False):
self.model_disconnected = True
logging.exception(_("model server went away"))
diff --git a/nova/tests/api/test_wsgi.py b/nova/tests/api/test_wsgi.py
index b1a849cf9..1ecdd1cfb 100644
--- a/nova/tests/api/test_wsgi.py
+++ b/nova/tests/api/test_wsgi.py
@@ -80,7 +80,7 @@ class ControllerTest(test.TestCase):
"attributes": {
"test": ["id"]}}}
- def show(self, req, id): # pylint: disable-msg=W0622,C0103
+ def show(self, req, id): # pylint: disable=W0622,C0103
return {"test": {"id": id}}
def __init__(self):
diff --git a/nova/tests/hyperv_unittest.py b/nova/tests/hyperv_unittest.py
index 3980ae3cb..042819b9c 100644
--- a/nova/tests/hyperv_unittest.py
+++ b/nova/tests/hyperv_unittest.py
@@ -51,7 +51,7 @@ class HyperVTestCase(test.TestCase):
instance_ref = db.instance_create(self.context, instance)
conn = hyperv.get_connection(False)
- conn._create_vm(instance_ref) # pylint: disable-msg=W0212
+ conn._create_vm(instance_ref) # pylint: disable=W0212
found = [n for n in conn.list_instances()
if n == instance_ref['name']]
self.assertTrue(len(found) == 1)
diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py
index 5a1be08eb..5d160bdf8 100644
--- a/nova/tests/objectstore_unittest.py
+++ b/nova/tests/objectstore_unittest.py
@@ -179,7 +179,7 @@ class ObjectStoreTestCase(test.TestCase):
class TestHTTPChannel(http.HTTPChannel):
"""Dummy site required for twisted.web"""
- def checkPersistence(self, _, __): # pylint: disable-msg=C0103
+ def checkPersistence(self, _, __): # pylint: disable=C0103
"""Otherwise we end up with an unclean reactor."""
return False
@@ -209,7 +209,7 @@ class S3APITestCase(test.TestCase):
root = S3()
self.site = TestSite(root)
- # pylint: disable-msg=E1101
+ # pylint: disable=E1101
self.listening_port = reactor.listenTCP(0, self.site,
interface='127.0.0.1')
# pylint: enable-msg=E1101
@@ -231,11 +231,11 @@ class S3APITestCase(test.TestCase):
self.conn.get_http_connection = get_http_connection
- def _ensure_no_buckets(self, buckets): # pylint: disable-msg=C0111
+ def _ensure_no_buckets(self, buckets): # pylint: disable=C0111
self.assertEquals(len(buckets), 0, "Bucket list was not empty")
return True
- def _ensure_one_bucket(self, buckets, name): # pylint: disable-msg=C0111
+ def _ensure_one_bucket(self, buckets, name): # pylint: disable=C0111
self.assertEquals(len(buckets), 1,
"Bucket list didn't have exactly one element in it")
self.assertEquals(buckets[0].name, name, "Wrong name")
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index d5c54a1c3..b67d6b12c 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -124,7 +124,7 @@ class ApiEc2TestCase(test.TestCase):
self.mox.StubOutWithMock(self.ec2, 'new_http_connection')
self.http = FakeHttplibConnection(
self.app, '%s:8773' % (self.host), False)
- # pylint: disable-msg=E1103
+ # pylint: disable=E1103
self.ec2.new_http_connection(host, is_secure).AndReturn(self.http)
return self.http
diff --git a/nova/tests/test_middleware.py b/nova/tests/test_middleware.py
index 9d49167ba..6564a6955 100644
--- a/nova/tests/test_middleware.py
+++ b/nova/tests/test_middleware.py
@@ -40,12 +40,12 @@ def conditional_forbid(req):
class LockoutTestCase(test.TestCase):
"""Test case for the Lockout middleware."""
- def setUp(self): # pylint: disable-msg=C0103
+ def setUp(self): # pylint: disable=C0103
super(LockoutTestCase, self).setUp()
utils.set_time_override()
self.lockout = ec2.Lockout(conditional_forbid)
- def tearDown(self): # pylint: disable-msg=C0103
+ def tearDown(self): # pylint: disable=C0103
utils.clear_time_override()
super(LockoutTestCase, self).tearDown()
diff --git a/po/nova.pot b/po/nova.pot
index ce88d731b..58140302d 100644
--- a/po/nova.pot
+++ b/po/nova.pot
@@ -300,7 +300,7 @@ msgstr ""
msgid "instance %s: starting..."
msgstr ""
-#. pylint: disable-msg=W0702
+#. pylint: disable=W0702
#: ../nova/compute/manager.py:219
#, python-format
msgid "instance %s: Failed to spawn"
@@ -440,7 +440,7 @@ msgid ""
"instance %(instance_id)s: attaching volume %(volume_id)s to %(mountpoint)s"
msgstr ""
-#. pylint: disable-msg=W0702
+#. pylint: disable=W0702
#. NOTE(vish): The inline callback eats the exception info so we
#. log the traceback here and reraise the same
#. ecxception below.
@@ -591,7 +591,7 @@ msgstr ""
msgid "Starting Bridge interface for %s"
msgstr ""
-#. pylint: disable-msg=W0703
+#. pylint: disable=W0703
#: ../nova/network/linux_net.py:314
#, python-format
msgid "Hupping dnsmasq threw %s"
@@ -602,7 +602,7 @@ msgstr ""
msgid "Pid %d is stale, relaunching dnsmasq"
msgstr ""
-#. pylint: disable-msg=W0703
+#. pylint: disable=W0703
#: ../nova/network/linux_net.py:358
#, python-format
msgid "killing radvd threw %s"
@@ -613,7 +613,7 @@ msgstr ""
msgid "Pid %d is stale, relaunching radvd"
msgstr ""
-#. pylint: disable-msg=W0703
+#. pylint: disable=W0703
#: ../nova/network/linux_net.py:449
#, python-format
msgid "Killing dnsmasq threw %s"
diff --git a/tools/euca-get-ajax-console b/tools/euca-get-ajax-console
index e407dd566..3df3dcb53 100755
--- a/tools/euca-get-ajax-console
+++ b/tools/euca-get-ajax-console
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
--
cgit
From 204ec967ee46079fb95a18fcfb1167ff57458015 Mon Sep 17 00:00:00 2001
From: Brian Lamar
Date: Fri, 18 Mar 2011 09:56:38 -0400
Subject: enable-msg -> enable
---
nova/auth/fakeldap.py | 2 +-
nova/tests/objectstore_unittest.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/auth/fakeldap.py b/nova/auth/fakeldap.py
index e8f5771d5..79afb9109 100644
--- a/nova/auth/fakeldap.py
+++ b/nova/auth/fakeldap.py
@@ -277,7 +277,7 @@ class FakeLDAP(object):
attrs = dict([(k, v) for k, v in attrs.iteritems()
if not fields or k in fields])
objects.append((key[len(self.__prefix):], attrs))
- # pylint: enable-msg=E1103
+ # pylint: enable=E1103
if objects == []:
raise NO_SUCH_OBJECT()
return objects
diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py
index 5d160bdf8..4e2ac205e 100644
--- a/nova/tests/objectstore_unittest.py
+++ b/nova/tests/objectstore_unittest.py
@@ -212,7 +212,7 @@ class S3APITestCase(test.TestCase):
# pylint: disable=E1101
self.listening_port = reactor.listenTCP(0, self.site,
interface='127.0.0.1')
- # pylint: enable-msg=E1101
+ # pylint: enable=E1101
self.tcp_port = self.listening_port.getHost().port
if not boto.config.has_section('Boto'):
--
cgit
From a50deeb264ff721584d5b0a6ace749d8e2c44842 Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: Fri, 18 Mar 2011 10:10:46 -0400
Subject: Change cloud.id_to_ec2_id to ec2utils.id_to_ec2_id. Fixes EC2 API
error handling when invalid instances and volume names are specified.
---
nova/api/ec2/__init__.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py
index fccebca5d..20701cfa8 100644
--- a/nova/api/ec2/__init__.py
+++ b/nova/api/ec2/__init__.py
@@ -31,7 +31,7 @@ from nova import log as logging
from nova import utils
from nova import wsgi
from nova.api.ec2 import apirequest
-from nova.api.ec2 import cloud
+from nova.api.ec2 import ec2utils
from nova.auth import manager
@@ -319,13 +319,13 @@ class Executor(wsgi.Application):
except exception.InstanceNotFound as ex:
LOG.info(_('InstanceNotFound raised: %s'), unicode(ex),
context=context)
- ec2_id = cloud.id_to_ec2_id(ex.instance_id)
+ ec2_id = ec2utils.id_to_ec2_id(ex.instance_id)
message = _('Instance %s not found') % ec2_id
return self._error(req, context, type(ex).__name__, message)
except exception.VolumeNotFound as ex:
LOG.info(_('VolumeNotFound raised: %s'), unicode(ex),
context=context)
- ec2_id = cloud.id_to_ec2_id(ex.volume_id, 'vol-%08x')
+ ec2_id = ec2utils.id_to_ec2_id(ex.volume_id, 'vol-%08x')
message = _('Volume %s not found') % ec2_id
return self._error(req, context, type(ex).__name__, message)
except exception.NotFound as ex:
--
cgit
From dba79cdf18f20f1e4e0758ae19b33de94881e440 Mon Sep 17 00:00:00 2001
From: Dan Prince
Date: Fri, 18 Mar 2011 11:12:44 -0400
Subject: Added test case.
---
nova/tests/test_api.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index d5c54a1c3..7023eb410 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -20,6 +20,7 @@
import boto
from boto.ec2 import regioninfo
+from boto.exception import EC2ResponseError
import datetime
import httplib
import random
@@ -177,6 +178,17 @@ class ApiEc2TestCase(test.TestCase):
self.manager.delete_project(project)
self.manager.delete_user(user)
+ def test_terminate_invalid_instance(self):
+ """Attempt to terminate an invalid instance"""
+ self.expect_http()
+ self.mox.ReplayAll()
+ user = self.manager.create_user('fake', 'fake', 'fake')
+ project = self.manager.create_project('fake', 'fake', 'fake')
+ self.assertRaises(EC2ResponseError, self.ec2.terminate_instances,
+ "i-00000005")
+ self.manager.delete_project(project)
+ self.manager.delete_user(user)
+
def test_get_all_key_pairs(self):
"""Test that, after creating a user and project and generating
a key pair, that the API call to list key pairs works properly"""
--
cgit
From 4f5dc6314f9dd7bb136a38fa07b109eb2e12734d Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Fri, 18 Mar 2011 10:06:36 -0700
Subject: auth_data is a list now (thanks Rick!)
---
nova/tests/api/openstack/fakes.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 5decb2bad..020682093 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -240,7 +240,8 @@ class FakeAuthManager(object):
@classmethod
def reset_fake_data(cls):
- cls.auth_data = dict(u1=User('id1', 'guy1', 'acc1', 'secret1', False))
+ u1 = User('id1', 'guy1', 'acc1', 'secret1', False)
+ cls.auth_data = [u1]
cls.projects = dict(testacct=Project('testacct',
'testacct',
'id1',
--
cgit
From ee09125e31a3afe64f0a9540a88fdb5febd7ddd4 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Fri, 18 Mar 2011 10:14:42 -0700
Subject: Avoid single-letter variable names
---
nova/tests/api/openstack/fakes.py | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 020682093..9b5ed8a15 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -255,21 +255,21 @@ class FakeAuthManager(object):
return FakeAuthManager.auth_data
def get_user(self, uid):
- for u in FakeAuthManager.auth_data:
- if u.id == uid:
- return u
+ for user in FakeAuthManager.auth_data:
+ if user.id == uid:
+ return user
return None
def get_user_from_access_key(self, key):
- for u in FakeAuthManager.auth_data:
- if u.access == key:
- return u
+ for user in FakeAuthManager.auth_data:
+ if user.access == key:
+ return user
return None
def delete_user(self, uid):
- for u in FakeAuthManager.auth_data:
- if u.id == uid:
- FakeAuthManager.auth_data.remove(u)
+ for user in FakeAuthManager.auth_data:
+ if user.id == uid:
+ FakeAuthManager.auth_data.remove(user)
return None
def create_user(self, name, access=None, secret=None, admin=False):
@@ -278,10 +278,7 @@ class FakeAuthManager(object):
return u
def modify_user(self, user_id, access=None, secret=None, admin=None):
- user = None
- for u in FakeAuthManager.auth_data:
- if u.id == user_id:
- user = u
+ user = self.get_user(user_id)
if user:
user.access = access
user.secret = secret
--
cgit
From 48a1423081355b49340aa1a4a37361654d9c0d87 Mon Sep 17 00:00:00 2001
From: Justin Santa Barbara
Date: Fri, 18 Mar 2011 10:24:06 -0700
Subject: A few more single-letter variable names bite the dust
---
nova/tests/api/openstack/test_auth.py | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/nova/tests/api/openstack/test_auth.py b/nova/tests/api/openstack/test_auth.py
index 446c5c149..21596fb25 100644
--- a/nova/tests/api/openstack/test_auth.py
+++ b/nova/tests/api/openstack/test_auth.py
@@ -51,8 +51,8 @@ class Test(test.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
- f.add_user(u)
+ user = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
+ f.add_user(user)
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'user1'
@@ -66,9 +66,9 @@ class Test(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
- f.add_user(u)
- f.create_project('user1_project', u)
+ user = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
+ f.add_user(user)
+ f.create_project('user1_project', user)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'user1'
@@ -124,8 +124,8 @@ class Test(test.TestCase):
def test_bad_user_good_key(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
- f.add_user(u)
+ user = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
+ f.add_user(user)
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'unknown_user'
@@ -190,9 +190,9 @@ class TestLimiter(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
- u = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
- f.add_user(u)
- f.create_project('test', u)
+ user = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
+ f.add_user(user)
+ f.create_project('test', user)
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'user1'
--
cgit
From a0052203c7cc957677293e53ea7c0191d0493ea8 Mon Sep 17 00:00:00 2001
From: Ken Pepple
Date: Fri, 18 Mar 2011 12:18:15 -0700
Subject: uses True/False instead of 1/0 for Postgres compatibility
---
nova/db/api.py | 2 +-
nova/db/sqlalchemy/api.py | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/nova/db/api.py b/nova/db/api.py
index 3cb0e5811..dd78fa3e7 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -1118,7 +1118,7 @@ def instance_type_create(context, values):
return IMPL.instance_type_create(context, values)
-def instance_type_get_all(context, inactive=0):
+def instance_type_get_all(context, inactive=False):
"""Get all instance types"""
return IMPL.instance_type_get_all(context, inactive)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 9d9b86c1d..e72be0e0c 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -2337,7 +2337,7 @@ def instance_type_create(_context, values):
@require_context
-def instance_type_get_all(context, inactive=0):
+def instance_type_get_all(context, inactive=False):
"""
Returns a dict describing all instance_types with name as key.
"""
@@ -2392,7 +2392,7 @@ def instance_type_destroy(context, name):
session = get_session()
instance_type_ref = session.query(models.InstanceTypes).\
filter_by(name=name)
- records = instance_type_ref.update(dict(deleted=1))
+ records = instance_type_ref.update(dict(deleted=True))
if records == 0:
raise exception.NotFound
else:
--
cgit
From 8437d947a6e94baf7aa53746ffd34aa5c0f521d9 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Fri, 18 Mar 2011 15:34:50 -0500
Subject: Better errors when virt driver isn't loaded
---
nova/compute/manager.py | 12 ++++++++++--
plugins/xenserver/xenapi/etc/xapi.d/plugins/glance | 3 +--
2 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 92deca813..5b8e4dafb 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -39,6 +39,7 @@ import os
import random
import string
import socket
+import sys
import tempfile
import time
import functools
@@ -114,7 +115,13 @@ class ComputeManager(manager.Manager):
# and redocument the module docstring
if not compute_driver:
compute_driver = FLAGS.compute_driver
- self.driver = utils.import_object(compute_driver)
+
+ try:
+ self.driver = utils.import_object(compute_driver)
+ except ImportError:
+ LOG.error("Unable to load the virtualization driver.")
+ sys.exit(1)
+
self.network_manager = utils.import_object(FLAGS.network_manager)
self.volume_manager = utils.import_object(FLAGS.volume_manager)
super(ComputeManager, self).__init__(*args, **kwargs)
@@ -221,7 +228,8 @@ class ComputeManager(manager.Manager):
instance_id,
{'launched_at': now})
except Exception: # pylint: disable-msg=W0702
- LOG.exception(_("instance %s: Failed to spawn"), instance_id,
+ LOG.exception(_("Instance '%s' failed to spawn. Is virtualization"
+ "enabled in the BIOS?"), instance_id,
context=context)
self.db.instance_set_state(context,
instance_id,
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
index c996f6ef4..0a45f3873 100644
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/glance
@@ -216,8 +216,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port, os_type):
'x-image-meta-status': 'queued',
'x-image-meta-disk-format': 'vhd',
'x-image-meta-container-format': 'ovf',
- 'x-image-meta-property-os-type': os_type
- }
+ 'x-image-meta-property-os-type': os_type}
for header, value in headers.iteritems():
conn.putheader(header, value)
--
cgit
From d6589138bd06e19851594e62fc515b964596bf61 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Fri, 18 Mar 2011 14:34:37 -0700
Subject: Fixed netadmin smoketests for ipv6
---
contrib/boto_v6/ec2/connection.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/contrib/boto_v6/ec2/connection.py b/contrib/boto_v6/ec2/connection.py
index faecae95e..868c93c11 100644
--- a/contrib/boto_v6/ec2/connection.py
+++ b/contrib/boto_v6/ec2/connection.py
@@ -4,8 +4,10 @@ Created on 2010/12/20
@author: Nachi Ueno
'''
import boto
+import base64
import boto.ec2
from boto_v6.ec2.instance import ReservationV6
+from boto.ec2.securitygroup import SecurityGroup
class EC2ConnectionV6(boto.ec2.EC2Connection):
@@ -101,7 +103,7 @@ class EC2ConnectionV6(boto.ec2.EC2Connection):
with the Image.
:rtype: Reservation
- :return: The :class:`boto.ec2.instance.Reservation`
+ :return: The :class:`boto.ec2.instance.ReservationV6`
associated with the request for machines
"""
params = {'ImageId': image_id,
--
cgit
From ac66fde6d787742e9d5d6af9ebfe3302d9375073 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Fri, 18 Mar 2011 17:22:13 -0500
Subject: comment more descriptive
---
nova/virt/xenapi/vmops.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 6542630c1..cfd74e5de 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -734,7 +734,7 @@ class VMOps(object):
"""
logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref)
- # make sure we have a vm opaque ref (raises otherwise)
+ # this function raises if vm_ref is not a vm_opaque_ref
self._session.get_xenapi().VM.get_record(vm_ref)
for (network, info) in network_info:
@@ -754,7 +754,7 @@ class VMOps(object):
"""Creates vifs for an instance"""
logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
- # make sure we have a vm opaque ref (raises otherwise)
+ # this function raises if vm_ref is not a vm_opaque_ref
self._session.get_xenapi().VM.get_record(vm_ref)
device = 0
--
cgit
From 3113a9c523a37c777164b7d1216e1df61bd3f825 Mon Sep 17 00:00:00 2001
From: Ken Pepple
Date: Fri, 18 Mar 2011 16:28:53 -0700
Subject: fixed migration instance_types migration to support postgres
correctly
---
nova/db/sqlalchemy/migrate_repo/versions/008_add_instance_types.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
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 66609054e..5e2cb69d9 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
@@ -55,7 +55,7 @@ def upgrade(migrate_engine):
try:
instance_types.create()
except Exception:
- logging.info(repr(table))
+ logging.info(repr(instance_types))
logging.exception('Exception while creating instance_types table')
raise
@@ -72,11 +72,11 @@ def upgrade(migrate_engine):
# FIXME(kpepple) should we be seeding created_at / updated_at ?
# now = datetime.datatime.utcnow()
i.execute({'name': name, 'memory_mb': values["memory_mb"],
- 'vcpus': values["vcpus"], 'deleted': 0,
+ 'vcpus': values["vcpus"], 'deleted': False,
'local_gb': values["local_gb"],
'flavorid': values["flavorid"]})
except Exception:
- logging.info(repr(table))
+ logging.info(repr(instance_types))
logging.exception('Exception while seeding instance_types table')
raise
--
cgit
From 157b4d67ae2cfb7cda6cf145a5803ff83b848075 Mon Sep 17 00:00:00 2001
From: Ken Pepple
Date: Fri, 18 Mar 2011 16:50:08 -0700
Subject: fix nova-manage instance_type list for postgres compatibility
---
nova/db/sqlalchemy/api.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 5430f89f9..3bf4f5eb8 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -2353,7 +2353,7 @@ def instance_type_get_all(context, inactive=False):
all()
else:
inst_types = session.query(models.InstanceTypes).\
- filter_by(deleted=inactive).\
+ filter_by(deleted=False).\
order_by("name").\
all()
if inst_types:
--
cgit
From a3fe673108602e27cca132209e87369fa8bf1323 Mon Sep 17 00:00:00 2001
From: Tushar Patil
Date: Fri, 18 Mar 2011 19:46:04 -0700
Subject: Changed Copyright to NTT for newly added files for flatmanager ipv6
---
.../versions/012_add_ipv6_flatmanager.py | 2 +-
nova/tests/network/__init__.py | 20 ++++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
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 5f5e3d007..8c9cf3377 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
@@ -1,4 +1,4 @@
-# Copyright 2010 OpenStack LLC.
+# Copyright (c) 2011 NTT.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
diff --git a/nova/tests/network/__init__.py b/nova/tests/network/__init__.py
index e0d479f8c..97f96b6fa 100644
--- a/nova/tests/network/__init__.py
+++ b/nova/tests/network/__init__.py
@@ -1,3 +1,23 @@
+# 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.
+"""
+Utility methods
+"""
import os
from nova import context
--
cgit
From bdbdc3fc49e3885df6dbfe75badab35f5fd15c8d Mon Sep 17 00:00:00 2001
From: Ken Pepple
Date: Fri, 18 Mar 2011 23:10:14 -0700
Subject: cleanup another inconsistent use of 1 for True in nova-manage
---
bin/nova-manage | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 6dcdddd5e..013a6077b 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -874,7 +874,7 @@ class InstanceTypeCommands(object):
if name == None:
inst_types = instance_types.get_all_types()
elif name == "--all":
- inst_types = instance_types.get_all_types(1)
+ inst_types = instance_types.get_all_types(True)
else:
inst_types = instance_types.get_instance_type(name)
except exception.DBError, e:
--
cgit
From 98b0fd564ca86a7b38bca149b28a837c8aa2d1e8 Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Sun, 20 Mar 2011 20:06:22 +0100
Subject: pep8
---
nova/tests/api/openstack/test_servers.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index bb33ec03d..efba2970f 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -1174,5 +1174,3 @@ class TestServerInstanceCreation(test.TestCase):
server = dom.childNodes[0]
self.assertEquals(server.nodeName, 'server')
self.assertTrue(server.getAttribute('adminPass').startswith('fake'))
-
-
--
cgit
From 9192e80d1161814e7b14946a5bd5787e9a8cf31d Mon Sep 17 00:00:00 2001
From: Soren Hansen
Date: Mon, 21 Mar 2011 09:49:32 +0100
Subject: Wrap update_dhcp in utils.synchronized.
---
nova/network/linux_net.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py
index 565732869..ee36407a6 100644
--- a/nova/network/linux_net.py
+++ b/nova/network/linux_net.py
@@ -557,6 +557,7 @@ def get_dhcp_hosts(context, network_id):
# NOTE(ja): Sending a HUP only reloads the hostfile, so any
# configuration options (like dchp-range, vlan, ...)
# aren't reloaded.
+@utils.synchronized('dnsmasq_start')
def update_dhcp(context, network_id):
"""(Re)starts a dnsmasq server for a given network
--
cgit
--
cgit
From 665e155339b8c4498e39e783710d869dcfc94238 Mon Sep 17 00:00:00 2001
From: Josh Kleinpeter
Date: Mon, 21 Mar 2011 09:06:42 -0500
Subject: Added my name to Authors Added I18n for network create string
---
Authors | 1 +
bin/nova-manage | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/Authors b/Authors
index 9aad104a7..1679d2dee 100644
--- a/Authors
+++ b/Authors
@@ -33,6 +33,7 @@ Jonathan Bryce
Jordan Rinke
Josh Durgin
Josh Kearney
+Josh Kleinpeter
Joshua McKenty
Justin Santa Barbara
Kei Masumoto
diff --git a/bin/nova-manage b/bin/nova-manage
index 0c39b662c..53e954003 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -522,8 +522,8 @@ class NetworkCommands(object):
[network_size=FLAG], [vlan_start=FLAG],
[vpn_start=FLAG], [fixed_range_v6=FLAG]"""
if not fixed_range:
- raise ValueError('Fixed range in the form of 10.0.0.0/8 is '
- 'required to create networks.')
+ raise ValueError(_('Fixed range in the form of 10.0.0.0/8 is '
+ 'required to create networks.'))
if not num_networks:
num_networks = FLAGS.num_networks
if not network_size:
--
cgit
From 3754a7b6f4cf0e9c60a140348b4cdb9c8acde062 Mon Sep 17 00:00:00 2001
From: Josh Kleinpeter
Date: Mon, 21 Mar 2011 09:17:12 -0500
Subject: Changed error to TypeError so that we get the arguments list.
---
bin/nova-manage | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/bin/nova-manage b/bin/nova-manage
index 53e954003..bdc129077 100755
--- a/bin/nova-manage
+++ b/bin/nova-manage
@@ -522,8 +522,8 @@ class NetworkCommands(object):
[network_size=FLAG], [vlan_start=FLAG],
[vpn_start=FLAG], [fixed_range_v6=FLAG]"""
if not fixed_range:
- raise ValueError(_('Fixed range in the form of 10.0.0.0/8 is '
- 'required to create networks.'))
+ raise TypeError(_('Fixed range in the form of 10.0.0.0/8 is '
+ 'required to create networks.'))
if not num_networks:
num_networks = FLAGS.num_networks
if not network_size:
--
cgit
From b3bb847e3dc20611c4a975d3c772256700b2d018 Mon Sep 17 00:00:00 2001
From: Josh Kearney
Date: Mon, 21 Mar 2011 10:41:03 -0500
Subject: Added space
---
nova/compute/manager.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index ff33597ce..576937cd8 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -229,7 +229,7 @@ class ComputeManager(manager.Manager):
{'launched_at': now})
except Exception: # pylint: disable=W0702
LOG.exception(_("Instance '%s' failed to spawn. Is virtualization"
- "enabled in the BIOS?"), instance_id,
+ " enabled in the BIOS?"), instance_id,
context=context)
self.db.instance_set_state(context,
instance_id,
--
cgit
From 6a893eabc83f4561025a9a655b0aabb2d3e1b3a7 Mon Sep 17 00:00:00 2001
From: Trey Morris
Date: Mon, 21 Mar 2011 13:19:20 -0500
Subject: added an enumerate to track device in vmops.create_vifs()
---
nova/virt/xenapi/vmops.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index cfd74e5de..61ff00903 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -757,8 +757,7 @@ class VMOps(object):
# this function raises if vm_ref is not a vm_opaque_ref
self._session.get_xenapi().VM.get_record(vm_ref)
- device = 0
- for (network, info) in network_info:
+ for device, (network, info) in enumerate(network_info):
mac_address = info['mac']
bridge = network['bridge']
rxtx_cap = info.pop('rxtx_cap')
@@ -767,7 +766,6 @@ class VMOps(object):
VMHelper.create_vif(self._session, vm_ref, network_ref,
mac_address, device, rxtx_cap)
- device += 1
def reset_network(self, instance, vm_ref):
"""Creates uuid arg to pass to make_agent_call and calls it."""
--
cgit
From 0cff0a13bac3539a46b3b932bfd016df7f190196 Mon Sep 17 00:00:00 2001
From: Vishvananda Ishaya
Date: Mon, 21 Mar 2011 14:20:13 -0700
Subject: import greenthread in libvirt
---
nova/virt/libvirt_conn.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index e80b9fbdf..f57f1a675 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -46,10 +46,9 @@ import time
import uuid
from xml.dom import minidom
-
+from eventlet import greenthread
from eventlet import tpool
from eventlet import semaphore
-
import IPy
from nova import context
--
cgit
From 4b8ed5afd1fd3e616eda0015f9bf16c7097f5476 Mon Sep 17 00:00:00 2001
From: Todd Willey
Date: Tue, 22 Mar 2011 03:13:12 -0400
Subject: vpn changes
---
nova/api/ec2/admin.py | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py
index d9a4ef999..208fe5c4f 100644
--- a/nova/api/ec2/admin.py
+++ b/nova/api/ec2/admin.py
@@ -28,6 +28,7 @@ from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
+from nova.api.ec2 import ec2utils
from nova.auth import manager
@@ -92,15 +93,18 @@ def vpn_dict(project, vpn_instance):
'public_ip': project.vpn_ip,
'public_port': project.vpn_port}
if vpn_instance:
- rv['instance_id'] = vpn_instance['ec2_id']
+ rv['instance_id'] = ec2utils.id_to_ec2_id(vpn_instance['id'])
rv['created_at'] = utils.isotime(vpn_instance['created_at'])
address = vpn_instance.get('fixed_ip', None)
if address:
rv['internal_ip'] = address['address']
- if utils.vpn_ping(project.vpn_ip, project.vpn_port):
- rv['state'] = 'running'
+ if project.vpn_ip and project.vpn_port:
+ if utils.vpn_ping(project.vpn_ip, project.vpn_port):
+ rv['state'] = 'running'
+ else:
+ rv['state'] = 'down'
else:
- rv['state'] = 'down'
+ rv['state'] = 'down - invalid project vpn config'
else:
rv['state'] = 'pending'
return rv
@@ -279,7 +283,7 @@ class AdminController(object):
", ensure it isn't running, and try "
"again in a few minutes")
instance = self._vpn_for(context, project)
- return {'instance_id': instance['ec2_id']}
+ return {'instance_id': ec2utils.id_to_ec2_id(instance['id'])}
def describe_vpns(self, context):
vpns = []
--
cgit
From d06bce4b64b57551a722688a4038a4eaffa34278 Mon Sep 17 00:00:00 2001
From: Todd Willey
Date: Tue, 22 Mar 2011 16:34:02 -0400
Subject: typo fix.
---
nova/api/ec2/admin.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/nova/api/ec2/admin.py b/nova/api/ec2/admin.py
index d9a4ef999..f32d0804f 100644
--- a/nova/api/ec2/admin.py
+++ b/nova/api/ec2/admin.py
@@ -60,7 +60,7 @@ def project_dict(project):
def host_dict(host, compute_service, instances, volume_service, volumes, now):
"""Convert a host model object to a result dict"""
- rv = {'hostanme': host, 'instance_count': len(instances),
+ rv = {'hostname': host, 'instance_count': len(instances),
'volume_count': len(volumes)}
if compute_service:
latest = compute_service['updated_at'] or compute_service['created_at']
--
cgit
From 0dc2140d645d94d585fa8e3e5d189cd776574d28 Mon Sep 17 00:00:00 2001
From: Koji Iida
Date: Wed, 23 Mar 2011 13:14:54 +0900
Subject: Fix to avoid db migration failure in virtualenv
---
nova/db/sqlalchemy/migrate_repo/versions/012_add_ipv6_flatmanager.py | 3 +++
1 file changed, 3 insertions(+)
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 8c9cf3377..e87085668 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
@@ -26,6 +26,9 @@ 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,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
#
# Tables to alter
--
cgit