merged trunk

This commit is contained in:
Vishvananda Ishaya 2011-04-07 15:54:14 -07:00
commit be1cc1aee1
51 changed files with 571 additions and 225 deletions

View File

@ -878,7 +878,7 @@ class InstanceTypeCommands(object):
elif name == "--all":
inst_types = instance_types.get_all_types(True)
else:
inst_types = instance_types.get_instance_type(name)
inst_types = instance_types.get_instance_type_by_name(name)
except exception.DBError, e:
_db_error(e)
if isinstance(inst_types.values()[0], dict):

View File

@ -206,10 +206,14 @@ class ServiceWrapper(wsgi.Controller):
# NOTE(vish): make sure we have no unicode keys for py2.6.
params = dict([(str(k), v) for (k, v) in params.iteritems()])
result = method(context, **params)
if result is None or type(result) is str or type(result) is unicode:
return result
try:
return self._serialize(result, req.best_match_content_type())
content_type = req.best_match_content_type()
default_xmlns = self.get_default_xmlns(req)
return self._serialize(result, content_type, default_xmlns)
except:
raise exception.Error("returned non-serializable type: %s"
% result)

View File

@ -110,7 +110,8 @@ class CloudController(object):
'genrootca.sh')
start = os.getcwd()
os.makedirs(FLAGS.ca_path)
if not os.path.exists(FLAGS.ca_path):
os.makedirs(FLAGS.ca_path)
os.chdir(FLAGS.ca_path)
# TODO(vish): Do this with M2Crypto instead
utils.runthis(_("Generating root CA: %s"), "sh", genrootca_sh_path)
@ -729,7 +730,10 @@ class CloudController(object):
instance['project_id'],
instance['host'])
i['productCodesSet'] = self._convert_to_set([], 'product_codes')
i['instanceType'] = instance['instance_type']
if instance['instance_type']:
i['instanceType'] = instance['instance_type'].get('name')
else:
i['instanceType'] = None
i['launchTime'] = instance['created_at']
i['amiLaunchIndex'] = instance['launch_index']
i['displayName'] = instance['display_name']
@ -784,7 +788,7 @@ class CloudController(object):
def allocate_address(self, context, **kwargs):
LOG.audit(_("Allocate address"), context=context)
public_ip = self.network_api.allocate_floating_ip(context)
return {'addressSet': [{'publicIp': public_ip}]}
return {'publicIp': public_ip}
def release_address(self, context, public_ip, **kwargs):
LOG.audit(_("Release address %s"), public_ip, context=context)
@ -814,7 +818,7 @@ class CloudController(object):
ramdisk = self._get_image(context, kwargs['ramdisk_id'])
kwargs['ramdisk_id'] = ramdisk['id']
instances = self.compute_api.create(context,
instance_type=instance_types.get_by_type(
instance_type=instance_types.get_instance_type_by_name(
kwargs.get('instance_type', None)),
image_id=self._get_image(context, kwargs['image_id'])['id'],
min_count=int(kwargs.get('min_count', max_count)),

View File

@ -13,15 +13,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import common
import webob.exc
from nova import exception
from nova import flags
from nova import log as logging
from nova import wsgi
from nova.auth import manager
from nova.api.openstack import common
from nova.api.openstack import faults
FLAGS = flags.FLAGS
@ -35,7 +34,7 @@ def _translate_keys(account):
manager=account.project_manager_id)
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
_serialization_metadata = {
'application/xml': {

View File

@ -19,7 +19,7 @@ import time
from webob import exc
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
import nova.image.service
@ -29,7 +29,7 @@ def _translate_keys(inst):
return dict(backupSchedule=inst)
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
""" The backup schedule API controller for the Openstack API """
_serialization_metadata = {

View File

@ -22,6 +22,7 @@ import webob
from nova import exception
from nova import flags
from nova import log as logging
from nova import wsgi
LOG = logging.getLogger('common')
@ -30,6 +31,10 @@ LOG = logging.getLogger('common')
FLAGS = flags.FLAGS
XML_NS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
XML_NS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
def limited(items, request, max_limit=FLAGS.osapi_max_limit):
"""
Return a slice of items according to requested offset and limit.
@ -128,3 +133,9 @@ def get_id_from_href(href):
except:
LOG.debug(_("Error extracting id from href: %s") % href)
raise webob.exc.HTTPBadRequest(_('could not parse id from href'))
class OpenstackController(wsgi.Controller):
def get_default_xmlns(self, req):
# Use V10 by default
return XML_NS_V10

View File

@ -19,7 +19,7 @@ from webob import exc
from nova import console
from nova import exception
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
@ -43,7 +43,7 @@ def _translate_detail_keys(cons):
return dict(console=info)
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
"""The Consoles Controller for the Openstack API"""
_serialization_metadata = {

View File

@ -28,6 +28,7 @@ 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
@ -115,7 +116,7 @@ class ExtensionDescriptor(object):
return response_exts
class ActionExtensionController(wsgi.Controller):
class ActionExtensionController(common.OpenstackController):
def __init__(self, application):
@ -136,7 +137,7 @@ class ActionExtensionController(wsgi.Controller):
return res
class ResponseExtensionController(wsgi.Controller):
class ResponseExtensionController(common.OpenstackController):
def __init__(self, application):
self.application = application
@ -155,7 +156,8 @@ class ResponseExtensionController(wsgi.Controller):
body = res.body
headers = res.headers
except AttributeError:
body = self._serialize(res, content_type)
default_xmlns = None
body = self._serialize(res, content_type, default_xmlns)
headers = {"Content-Type": content_type}
res = webob.Response()
res.body = body
@ -163,7 +165,7 @@ class ResponseExtensionController(wsgi.Controller):
return res
class ExtensionController(wsgi.Controller):
class ExtensionController(common.OpenstackController):
def __init__(self, extension_manager):
self.extension_manager = extension_manager

View File

@ -20,10 +20,10 @@ import webob.dec
import webob.exc
from nova import wsgi
from nova.api.openstack import common
class Fault(webob.exc.HTTPException):
"""An RS API fault response."""
_fault_names = {
@ -47,7 +47,7 @@ class Fault(webob.exc.HTTPException):
"""Generate a WSGI response based on the exception passed to ctor."""
# Replace the body with fault details.
code = self.wrapped_exc.status_int
fault_name = self._fault_names.get(code, "computeFault")
fault_name = self._fault_names.get(code, "cloudServersFault")
fault_data = {
fault_name: {
'code': code,
@ -57,7 +57,8 @@ class Fault(webob.exc.HTTPException):
fault_data[fault_name]['retryAfter'] = retry
# 'code' is an attribute on the fault tag itself
metadata = {'application/xml': {'attributes': {fault_name: 'code'}}}
serializer = wsgi.Serializer(metadata)
default_xmlns = common.XML_NS_V10
serializer = wsgi.Serializer(metadata, default_xmlns)
content_type = req.best_match_content_type()
self.wrapped_exc.body = serializer.serialize(fault_data, content_type)
self.wrapped_exc.content_type = content_type

View File

@ -19,11 +19,11 @@ import webob
from nova import db
from nova import exception
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import views
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
"""Flavor controller for the OpenStack API."""
_serialization_metadata = {
@ -76,3 +76,6 @@ class ControllerV11(Controller):
def _get_view_builder(self, req):
base_url = req.application_url
return views.flavors.ViewBuilderV11(base_url)
def get_default_xmlns(self, req):
return common.XML_NS_V11

View File

@ -20,13 +20,14 @@ from webob import exc
from nova import flags
from nova import utils
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
FLAGS = flags.FLAGS
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
"""The image metadata API controller for the Openstack API"""
def __init__(self):

View File

@ -13,8 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import datetime
import webob.exc
from nova import compute
@ -22,7 +20,6 @@ from nova import exception
from nova import flags
from nova import log
from nova import utils
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
from nova.api.openstack.views import images as images_view
@ -32,7 +29,7 @@ LOG = log.getLogger('nova.api.openstack.images')
FLAGS = flags.FLAGS
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
"""Base `wsgi.Controller` for retrieving/displaying images."""
_serialization_metadata = {
@ -153,3 +150,6 @@ class ControllerV11(Controller):
"""Property to get the ViewBuilder class we need to use."""
base_url = request.application_url
return images_view.ViewBuilderV11(base_url)
def get_default_xmlns(self, req):
return common.XML_NS_V11

View File

@ -31,8 +31,8 @@ from collections import defaultdict
from webob.dec import wsgify
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
from nova.wsgi import Controller
from nova.wsgi import Middleware
@ -43,7 +43,7 @@ PER_HOUR = 60 * 60
PER_DAY = 60 * 60 * 24
class LimitsController(Controller):
class LimitsController(common.OpenstackController):
"""
Controller for accessing limits in the OpenStack API.
"""

View File

@ -19,10 +19,11 @@ from webob import exc
from nova import compute
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
""" The server metadata API controller for the Openstack API """
def __init__(self):

View File

@ -44,7 +44,7 @@ LOG = logging.getLogger('server')
FLAGS = flags.FLAGS
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
""" The Server API controller for the OpenStack API """
_serialization_metadata = {
@ -160,9 +160,11 @@ class Controller(wsgi.Controller):
name = name.strip()
try:
inst_type = \
instance_types.get_instance_type_by_flavor_id(flavor_id)
(inst,) = self.compute_api.create(
context,
instance_types.get_by_flavor_id(flavor_id),
inst_type,
image_id,
kernel_id=kernel_id,
ramdisk_id=ramdisk_id,
@ -175,7 +177,7 @@ class Controller(wsgi.Controller):
except quota.QuotaError as error:
self._handle_quota_error(error)
inst['instance_type'] = flavor_id
inst['instance_type'] = inst_type
inst['image_id'] = requested_image_id
builder = self._get_view_builder(req)
@ -648,6 +650,9 @@ class ControllerV11(Controller):
def _limit_items(self, items, req):
return common.limited_by_marker(items, req)
def get_default_xmlns(self, req):
return common.XML_NS_V11
class ServerCreateRequestXMLDeserializer(object):
"""

View File

@ -17,7 +17,7 @@
from webob import exc
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
@ -32,7 +32,7 @@ def _translate_detail_keys(inst):
return dict(sharedIpGroups=inst)
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
""" The Shared IP Groups Controller for the Openstack API """
_serialization_metadata = {

View File

@ -18,7 +18,6 @@ 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
@ -35,7 +34,7 @@ def _translate_keys(user):
admin=user.admin)
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
_serialization_metadata = {
'application/xml': {

View File

@ -115,7 +115,7 @@ class ViewBuilderV10(ViewBuilder):
def _build_flavor(self, response, inst):
if 'instance_type' in dict(inst):
response['flavorId'] = inst['instance_type']
response['flavorId'] = inst['instance_type']['flavorid']
class ViewBuilderV11(ViewBuilder):
@ -134,7 +134,7 @@ class ViewBuilderV11(ViewBuilder):
def _build_flavor(self, response, inst):
if "instance_type" in dict(inst):
flavor_id = inst["instance_type"]
flavor_id = inst["instance_type"]['flavorid']
flavor_ref = self.flavor_builder.generate_href(flavor_id)
response["flavorRef"] = flavor_ref

View File

@ -13,12 +13,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import common
from nova import db
from nova import flags
from nova import log as logging
from nova import wsgi
from nova.api.openstack import common
from nova.scheduler import api
@ -43,7 +41,7 @@ def _scrub_zone(zone):
'deleted', 'deleted_at', 'updated_at'))
class Controller(wsgi.Controller):
class Controller(common.OpenstackController):
_serialization_metadata = {
'application/xml': {

View File

@ -114,8 +114,11 @@ class API(base.Base):
"""Create the number of instances requested if quota and
other arguments check out ok."""
type_data = instance_types.get_instance_type(instance_type)
num_instances = quota.allowed_instances(context, max_count, type_data)
if not instance_type:
instance_type = instance_types.get_default_instance_type()
num_instances = quota.allowed_instances(context, max_count,
instance_type)
if num_instances < min_count:
pid = context.project_id
LOG.warn(_("Quota exceeeded for %(pid)s,"
@ -201,10 +204,10 @@ class API(base.Base):
'user_id': context.user_id,
'project_id': context.project_id,
'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
'instance_type': instance_type,
'memory_mb': type_data['memory_mb'],
'vcpus': type_data['vcpus'],
'local_gb': type_data['local_gb'],
'instance_type_id': instance_type['id'],
'memory_mb': instance_type['memory_mb'],
'vcpus': instance_type['vcpus'],
'local_gb': instance_type['local_gb'],
'display_name': display_name,
'display_description': display_description,
'user_data': user_data or '',
@ -521,8 +524,7 @@ class API(base.Base):
def resize(self, context, instance_id, flavor_id):
"""Resize a running instance."""
instance = self.db.instance_get(context, instance_id)
current_instance_type = self.db.instance_type_get_by_name(
context, instance['instance_type'])
current_instance_type = instance['instance_type']
new_instance_type = self.db.instance_type_get_by_flavor_id(
context, flavor_id)

View File

@ -59,8 +59,8 @@ def create(name, memory, vcpus, local_gb, flavorid, swap=0,
rxtx_quota=rxtx_quota,
rxtx_cap=rxtx_cap))
except exception.DBError, e:
LOG.exception(_('DB error: %s' % e))
raise exception.ApiError(_("Cannot create instance type: %s" % name))
LOG.exception(_('DB error: %s') % e)
raise exception.ApiError(_("Cannot create instance type: %s") % name)
def destroy(name):
@ -72,8 +72,8 @@ def destroy(name):
try:
db.instance_type_destroy(context.get_admin_context(), name)
except exception.NotFound:
LOG.exception(_('Instance type %s not found for deletion' % name))
raise exception.ApiError(_("Unknown instance type: %s" % name))
LOG.exception(_('Instance type %s not found for deletion') % name)
raise exception.ApiError(_("Unknown instance type: %s") % name)
def purge(name):
@ -85,8 +85,8 @@ def purge(name):
try:
db.instance_type_purge(context.get_admin_context(), name)
except exception.NotFound:
LOG.exception(_('Instance type %s not found for purge' % name))
raise exception.ApiError(_("Unknown instance type: %s" % name))
LOG.exception(_('Instance type %s not found for purge') % name)
raise exception.ApiError(_("Unknown instance type: %s") % name)
def get_all_types(inactive=0):
@ -101,41 +101,43 @@ def get_all_flavors():
return get_all_types(context.get_admin_context())
def get_instance_type(name):
def get_default_instance_type():
name = FLAGS.default_instance_type
try:
return get_instance_type_by_name(name)
except exception.DBError:
raise exception.ApiError(_("Unknown instance type: %s") % name)
def get_instance_type(id):
"""Retrieves single instance type by id"""
if id is None:
return get_default_instance_type()
try:
ctxt = context.get_admin_context()
return db.instance_type_get_by_id(ctxt, id)
except exception.DBError:
raise exception.ApiError(_("Unknown instance type: %s") % name)
def get_instance_type_by_name(name):
"""Retrieves single instance type by name"""
if name is None:
return FLAGS.default_instance_type
return get_default_instance_type()
try:
ctxt = context.get_admin_context()
inst_type = db.instance_type_get_by_name(ctxt, name)
return inst_type
return db.instance_type_get_by_name(ctxt, name)
except exception.DBError:
raise exception.ApiError(_("Unknown instance type: %s" % name))
raise exception.ApiError(_("Unknown instance type: %s") % name)
def get_by_type(instance_type):
"""retrieve instance type name"""
if instance_type is None:
return FLAGS.default_instance_type
try:
ctxt = context.get_admin_context()
inst_type = db.instance_type_get_by_name(ctxt, instance_type)
return inst_type['name']
except exception.DBError, e:
LOG.exception(_('DB error: %s' % e))
raise exception.ApiError(_("Unknown instance type: %s" %\
instance_type))
def get_by_flavor_id(flavor_id):
"""retrieve instance type's name by flavor_id"""
def get_instance_type_by_flavor_id(flavor_id):
"""retrieve instance type by flavor_id"""
if flavor_id is None:
return FLAGS.default_instance_type
return get_default_instance_type()
try:
ctxt = context.get_admin_context()
flavor = db.instance_type_get_by_flavor_id(ctxt, flavor_id)
return flavor['name']
return db.instance_type_get_by_flavor_id(ctxt, flavor_id)
except exception.DBError, e:
LOG.exception(_('DB error: %s' % e))
raise exception.ApiError(_("Unknown flavor: %s" % flavor_id))
LOG.exception(_('DB error: %s') % e)
raise exception.ApiError(_("Unknown flavor: %s") % flavor_id)

View File

@ -269,6 +269,8 @@ def _sign_csr(csr_text, ca_folder):
LOG.debug(_("Flags path: %s"), ca_folder)
start = os.getcwd()
# Change working dir to CA
if not os.path.exists(ca_folder):
os.makedirs(ca_folder)
os.chdir(ca_folder)
utils.execute('openssl', 'ca', '-batch', '-out', outbound, '-config',
'./openssl.cnf', '-infiles', inbound)

View File

@ -1124,6 +1124,11 @@ def instance_type_get_all(context, inactive=False):
return IMPL.instance_type_get_all(context, inactive)
def instance_type_get_by_id(context, id):
"""Get instance type by id"""
return IMPL.instance_type_get_by_id(context, id)
def instance_type_get_by_name(context, name):
"""Get instance type by name"""
return IMPL.instance_type_get_by_name(context, name)

View File

@ -831,6 +831,7 @@ def instance_get(context, instance_id, session=None):
options(joinedload('volumes')).\
options(joinedload_all('fixed_ip.network')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(id=instance_id).\
filter_by(deleted=can_read_deleted(context)).\
first()
@ -840,6 +841,7 @@ def instance_get(context, instance_id, session=None):
options(joinedload_all('security_groups.rules')).\
options(joinedload('volumes')).\
options(joinedload('metadata')).\
options(joinedload('instance_type')).\
filter_by(project_id=context.project_id).\
filter_by(id=instance_id).\
filter_by(deleted=False).\
@ -859,6 +861,7 @@ def instance_get_all(context):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
options(joinedload('instance_type')).\
filter_by(deleted=can_read_deleted(context)).\
all()
@ -870,6 +873,7 @@ def instance_get_all_by_user(context, user_id):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
options(joinedload('instance_type')).\
filter_by(deleted=can_read_deleted(context)).\
filter_by(user_id=user_id).\
all()
@ -882,6 +886,7 @@ def instance_get_all_by_host(context, host):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
options(joinedload('instance_type')).\
filter_by(host=host).\
filter_by(deleted=can_read_deleted(context)).\
all()
@ -896,6 +901,7 @@ def instance_get_all_by_project(context, project_id):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
options(joinedload('instance_type')).\
filter_by(project_id=project_id).\
filter_by(deleted=can_read_deleted(context)).\
all()
@ -910,6 +916,7 @@ def instance_get_all_by_reservation(context, reservation_id):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
options(joinedload('instance_type')).\
filter_by(reservation_id=reservation_id).\
filter_by(deleted=can_read_deleted(context)).\
all()
@ -918,6 +925,7 @@ def instance_get_all_by_reservation(context, reservation_id):
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload_all('fixed_ip.network')).\
options(joinedload('instance_type')).\
filter_by(project_id=context.project_id).\
filter_by(reservation_id=reservation_id).\
filter_by(deleted=False).\
@ -930,6 +938,7 @@ def instance_get_project_vpn(context, project_id):
return session.query(models.Instance).\
options(joinedload_all('fixed_ip.floating_ips')).\
options(joinedload('security_groups')).\
options(joinedload('instance_type')).\
filter_by(project_id=project_id).\
filter_by(image_id=FLAGS.vpn_image_id).\
filter_by(deleted=can_read_deleted(context)).\
@ -2370,6 +2379,19 @@ def instance_type_get_all(context, inactive=False):
raise exception.NotFound
@require_context
def instance_type_get_by_id(context, id):
"""Returns a dict describing specific instance_type"""
session = get_session()
inst_type = session.query(models.InstanceTypes).\
filter_by(id=id).\
first()
if not inst_type:
raise exception.NotFound(_("No instance type with id %s") % id)
else:
return dict(inst_type)
@require_context
def instance_type_get_by_name(context, name):
"""Returns a dict describing specific instance_type"""

View File

@ -0,0 +1,84 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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
# 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()
c_instance_type = Column('instance_type',
String(length=255, convert_unicode=False,
assert_unicode=None, unicode_error=None,
_warn_on_bytestring=False),
nullable=True)
c_instance_type_id = Column('instance_type_id',
String(length=255, convert_unicode=False,
assert_unicode=None, unicode_error=None,
_warn_on_bytestring=False),
nullable=True)
instance_types = Table('instance_types', meta,
Column('id', Integer(), primary_key=True, nullable=False),
Column('name',
String(length=255, convert_unicode=False, assert_unicode=None,
unicode_error=None, _warn_on_bytestring=False),
unique=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 = Table('instances', meta, autoload=True,
autoload_with=migrate_engine)
instances.create_column(c_instance_type_id)
recs = migrate_engine.execute(instance_types.select())
for row in recs:
type_id = row[0]
type_name = row[1]
migrate_engine.execute(instances.update()\
.where(instances.c.instance_type == type_name)\
.values(instance_type_id=type_id))
instances.c.instance_type.drop()
def downgrade(migrate_engine):
meta.bind = migrate_engine
instances = Table('instances', meta, autoload=True,
autoload_with=migrate_engine)
instances.create_column(c_instance_type)
recs = migrate_engine.execute(instance_types.select())
for row in recs:
type_id = row[0]
type_name = row[1]
migrate_engine.execute(instances.update()\
.where(instances.c.instance_type_id == type_id)\
.values(instance_type=type_name))
instances.c.instance_type_id.drop()

View File

@ -209,7 +209,7 @@ class Instance(BASE, NovaBase):
hostname = Column(String(255))
host = Column(String(255)) # , ForeignKey('hosts.id'))
instance_type = Column(String(255))
instance_type_id = Column(String(255))
user_data = Column(Text)
@ -268,6 +268,12 @@ class InstanceTypes(BASE, NovaBase):
rxtx_quota = Column(Integer, nullable=False, default=0)
rxtx_cap = Column(Integer, nullable=False, default=0)
instances = relationship(Instance,
backref=backref('instance_type', uselist=False),
foreign_keys=id,
primaryjoin='and_(Instance.instance_type_id == '
'InstanceTypes.id)')
class Volume(BASE, NovaBase):
"""Represents a block storage device that can be attached to a vm."""

View File

@ -391,6 +391,12 @@ def unbind_floating_ip(floating_ip):
'dev', FLAGS.public_interface)
def ensure_metadata_ip():
"""Sets up local metadata ip"""
_execute('sudo', 'ip', 'addr', 'add', '169.254.169.254/32',
'scope', 'link', 'dev', 'lo', check_exit_code=False)
def ensure_vlan_forward(public_ip, port, private_ip):
"""Sets up forwarding rules for vlan"""
iptables_manager.ipv4['filter'].add_rule("FORWARD",
@ -442,6 +448,7 @@ def ensure_vlan(vlan_num):
return interface
@utils.synchronized('ensure_bridge', external=True)
def ensure_bridge(bridge, interface, net_attrs=None):
"""Create a bridge unless it already exists.
@ -495,6 +502,8 @@ def ensure_bridge(bridge, interface, net_attrs=None):
fields = line.split()
if fields and fields[0] == "0.0.0.0" and fields[-1] == interface:
gateway = fields[1]
_execute('sudo', 'route', 'del', 'default', 'gw', gateway,
'dev', interface)
out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface,
'scope', 'global')
for line in out.split("\n"):
@ -504,7 +513,7 @@ def ensure_bridge(bridge, interface, net_attrs=None):
_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)
_execute('sudo', 'route', 'add', 'default', 'gw', gateway)
out, err = _execute('sudo', 'brctl', 'addif', bridge, interface,
check_exit_code=False)

View File

@ -126,6 +126,7 @@ class NetworkManager(manager.SchedulerDependentManager):
standalone service.
"""
self.driver.init_host()
self.driver.ensure_metadata_ip()
# Set up networking for the projects for which we're already
# the designated network host.
ctxt = context.get_admin_context()

View File

@ -53,13 +53,13 @@ class APITest(test.TestCase):
#api.application = succeed
api = self._wsgi_app(succeed)
resp = Request.blank('/').get_response(api)
self.assertFalse('computeFault' in resp.body, resp.body)
self.assertFalse('cloudServersFault' in resp.body, resp.body)
self.assertEqual(resp.status_int, 200, resp.body)
#api.application = raise_webob_exc
api = self._wsgi_app(raise_webob_exc)
resp = Request.blank('/').get_response(api)
self.assertFalse('computeFault' in resp.body, resp.body)
self.assertFalse('cloudServersFault' in resp.body, resp.body)
self.assertEqual(resp.status_int, 404, resp.body)
#api.application = raise_api_fault
@ -71,11 +71,11 @@ class APITest(test.TestCase):
#api.application = fail
api = self._wsgi_app(fail)
resp = Request.blank('/').get_response(api)
self.assertTrue('{"computeFault' in resp.body, resp.body)
self.assertTrue('{"cloudServersFault' in resp.body, resp.body)
self.assertEqual(resp.status_int, 500, resp.body)
#api.application = fail
api = self._wsgi_app(fail)
resp = Request.blank('/.xml').get_response(api)
self.assertTrue('<computeFault' in resp.body, resp.body)
self.assertTrue('<cloudServersFault' in resp.body, resp.body)
self.assertEqual(resp.status_int, 500, resp.body)

View File

@ -22,6 +22,7 @@ import webob.dec
import webob.exc
from nova import test
from nova.api.openstack import common
from nova.api.openstack import faults
@ -47,10 +48,10 @@ class TestFaults(test.TestCase):
response = request.get_response(fault)
expected = self._prepare_xml("""
<badRequest code="400">
<badRequest code="400" xmlns="%s">
<message>scram</message>
</badRequest>
""")
""" % common.XML_NS_V10)
actual = self._prepare_xml(response.body)
self.assertEqual(response.content_type, "application/xml")
@ -91,11 +92,11 @@ class TestFaults(test.TestCase):
response = request.get_response(fault)
expected = self._prepare_xml("""
<overLimit code="413">
<overLimit code="413" xmlns="%s">
<message>sorry</message>
<retryAfter>4</retryAfter>
</overLimit>
""")
""" % common.XML_NS_V10)
actual = self._prepare_xml(response.body)
self.assertEqual(expected, actual)

View File

@ -146,7 +146,7 @@ class LocalImageServiceTest(_BaseImageServiceTests):
for x in [1, 2, 3]:
tempfile.mkstemp(prefix='ami-', dir=self.tempdir)
# create some valid image directories names
for x in ["1485baed", "1a60f0ee", "3123a73d"]:
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))
@ -335,7 +335,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
name="public image"
updated="%(expected_now)s"
created="%(expected_now)s"
status="ACTIVE" />
status="ACTIVE"
xmlns="http://docs.rackspacecloud.com/servers/api/v1.0" />
""" % (locals()))
self.assertEqual(expected_image.toxml(), actual_image.toxml())
@ -353,7 +354,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
name="None"
updated="%(expected_now)s"
created="%(expected_now)s"
status="ACTIVE" />
status="ACTIVE"
xmlns="http://docs.rackspacecloud.com/servers/api/v1.0" />
""" % (locals()))
self.assertEqual(expected_image.toxml(), actual_image.toxml())
@ -372,7 +374,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
name="public image"
updated="%(expected_now)s"
created="%(expected_now)s"
status="ACTIVE">
status="ACTIVE"
xmlns="http://docs.openstack.org/compute/api/v1.1">
<links>
<link href="%(expected_href)s" rel="self"/>
<link href="%(expected_href)s" rel="bookmark"
@ -408,7 +411,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
self.assertEqual(404, response.status_int)
expected = minidom.parseString("""
<itemNotFound code="404">
<itemNotFound code="404"
xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
<message>
Image not found.
</message>
@ -441,8 +445,11 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
response = request.get_response(fakes.wsgi_app())
self.assertEqual(404, response.status_int)
# NOTE(justinsb): I believe this should still use the v1.0 XSD,
# because the element hasn't changed definition
expected = minidom.parseString("""
<itemNotFound code="404">
<itemNotFound code="404"
xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
<message>
Image not found.
</message>

View File

@ -136,10 +136,17 @@ class LimitsControllerTest(BaseLimitTestSuite):
request = self._get_index_request("application/xml")
response = request.get_response(self.controller)
expected = "<limits><rate/><absolute/></limits>"
body = response.body.replace("\n", "").replace(" ", "")
expected = parseString("""
<limits
xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
<rate/>
<absolute/>
</limits>
""".replace(" ", ""))
self.assertEqual(expected, body)
body = parseString(response.body.replace(" ", ""))
self.assertEqual(expected.toxml(), body.toxml())
def test_index_xml(self):
"""Test getting limit details in XML."""
@ -148,7 +155,8 @@ class LimitsControllerTest(BaseLimitTestSuite):
response = request.get_response(self.controller)
expected = parseString("""
<limits>
<limits
xmlns="http://docs.rackspacecloud.com/servers/api/v1.0">
<rate>
<limit URI="*" regex=".*" remaining="10" resetTime="0"
unit="MINUTE" value="10" verb="GET"/>

View File

@ -32,6 +32,7 @@ from nova import test
import nova.api.openstack
from nova.api.openstack import servers
import nova.compute.api
from nova.compute import instance_types
import nova.db.api
from nova.db.sqlalchemy.models import Instance
from nova.db.sqlalchemy.models import InstanceMetadata
@ -71,13 +72,19 @@ def instance_address(context, instance_id):
return None
def stub_instance(id, user_id=1, private_address=None, public_addresses=None):
def stub_instance(id, user_id=1, private_address=None, public_addresses=None,
host=None):
metadata = []
metadata.append(InstanceMetadata(key='seq', value=id))
inst_type = instance_types.get_instance_type_by_flavor_id(1)
if public_addresses == None:
public_addresses = list()
if host != None:
host = str(host)
instance = {
"id": id,
"admin_pass": "",
@ -95,8 +102,8 @@ def stub_instance(id, user_id=1, private_address=None, public_addresses=None):
"vcpus": 0,
"local_gb": 0,
"hostname": "",
"host": None,
"instance_type": "1",
"host": host,
"instance_type": dict(inst_type),
"user_data": "",
"reservation_id": "",
"mac_address": "",
@ -628,7 +635,7 @@ class ServersTest(test.TestCase):
self.assertEqual(s['hostId'], '')
self.assertEqual(s['name'], 'server%d' % i)
self.assertEqual(s['imageId'], '10')
self.assertEqual(s['flavorId'], '1')
self.assertEqual(s['flavorId'], 1)
self.assertEqual(s['status'], 'BUILD')
self.assertEqual(s['metadata']['seq'], i)
@ -654,12 +661,8 @@ class ServersTest(test.TestCase):
instances - 2 on one host and 3 on another.
'''
def stub_instance(id, user_id=1):
return Instance(id=id, state=0, image_id=10, user_id=user_id,
display_name='server%s' % id, host='host%s' % (id % 2))
def return_servers_with_host(context, user_id=1):
return [stub_instance(i) for i in xrange(5)]
return [stub_instance(i, 1, None, None, i % 2) for i in xrange(5)]
self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
return_servers_with_host)
@ -677,7 +680,8 @@ class ServersTest(test.TestCase):
self.assertEqual(s['id'], i)
self.assertEqual(s['hostId'], host_ids[i % 2])
self.assertEqual(s['name'], 'server%d' % i)
self.assertEqual(s['imageId'], 10)
self.assertEqual(s['imageId'], '10')
self.assertEqual(s['flavorId'], 1)
def test_server_pause(self):
FLAGS.allow_admin_api = True

View File

@ -28,29 +28,34 @@ def stub_out_db_instance_api(stubs, injected=True):
"""Stubs out the db API for creating Instances."""
INSTANCE_TYPES = {
'm1.tiny': dict(memory_mb=512,
'm1.tiny': dict(id=2,
memory_mb=512,
vcpus=1,
local_gb=0,
flavorid=1,
rxtx_cap=1),
'm1.small': dict(memory_mb=2048,
'm1.small': dict(id=5,
memory_mb=2048,
vcpus=1,
local_gb=20,
flavorid=2,
rxtx_cap=2),
'm1.medium':
dict(memory_mb=4096,
dict(id=1,
memory_mb=4096,
vcpus=2,
local_gb=40,
flavorid=3,
rxtx_cap=3),
'm1.large': dict(memory_mb=8192,
'm1.large': dict(id=3,
memory_mb=8192,
vcpus=4,
local_gb=80,
flavorid=4,
rxtx_cap=4),
'm1.xlarge':
dict(memory_mb=16384,
dict(id=4,
memory_mb=16384,
vcpus=8,
local_gb=160,
flavorid=5,
@ -107,6 +112,12 @@ def stub_out_db_instance_api(stubs, injected=True):
def fake_instance_type_get_by_name(context, name):
return INSTANCE_TYPES[name]
def fake_instance_type_get_by_id(context, id):
for name, inst_type in INSTANCE_TYPES.iteritems():
if str(inst_type['id']) == str(id):
return inst_type
return None
def fake_network_get_by_instance(context, instance_id):
# Even instance numbers are on vlan networks
if instance_id % 2 == 0:
@ -136,6 +147,7 @@ def stub_out_db_instance_api(stubs, injected=True):
fake_network_get_all_by_instance)
stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all)
stubs.Set(db, 'instance_type_get_by_name', fake_instance_type_get_by_name)
stubs.Set(db, 'instance_type_get_by_id', fake_instance_type_get_by_id)
stubs.Set(db, 'instance_get_fixed_address',
fake_instance_get_fixed_address)
stubs.Set(db, 'instance_get_fixed_address_v6',

View File

@ -0,0 +1,56 @@
# 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.
from nova import flags
from nova.log import logging
from nova.tests.integrated import integrated_helpers
from nova.api.openstack import common
LOG = logging.getLogger('nova.tests.integrated')
FLAGS = flags.FLAGS
FLAGS.verbose = True
class XmlTests(integrated_helpers._IntegratedTestBase):
""""Some basic XML sanity checks."""
def test_namespace_limits(self):
"""/limits should have v1.0 namespace (hasn't changed in 1.1)."""
headers = {}
headers['Accept'] = 'application/xml'
response = self.api.api_request('/limits', headers=headers)
data = response.read()
LOG.debug("data: %s" % data)
prefix = '<limits xmlns="%s"' % common.XML_NS_V10
self.assertTrue(data.startswith(prefix))
def test_namespace_servers(self):
"""/servers should have v1.1 namespace (has changed in 1.1)."""
headers = {}
headers['Accept'] = 'application/xml'
response = self.api.api_request('/servers', headers=headers)
data = response.read()
LOG.debug("data: %s" % data)
prefix = '<servers xmlns="%s"' % common.XML_NS_V11
self.assertTrue(data.startswith(prefix))

View File

@ -84,7 +84,8 @@ class ComputeTestCase(test.TestCase):
inst['launch_time'] = '10'
inst['user_id'] = self.user.id
inst['project_id'] = self.project.id
inst['instance_type'] = 'm1.tiny'
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
inst['instance_type_id'] = type_id
inst['mac_address'] = utils.generate_mac()
inst['ami_launch_index'] = 0
inst.update(params)
@ -132,7 +133,7 @@ class ComputeTestCase(test.TestCase):
cases = [dict(), dict(display_name=None)]
for instance in cases:
ref = self.compute_api.create(self.context,
FLAGS.default_instance_type, None, **instance)
instance_types.get_default_instance_type(), None, **instance)
try:
self.assertNotEqual(ref[0]['display_name'], None)
finally:
@ -143,7 +144,7 @@ class ComputeTestCase(test.TestCase):
group = self._create_group()
ref = self.compute_api.create(
self.context,
instance_type=FLAGS.default_instance_type,
instance_type=instance_types.get_default_instance_type(),
image_id=None,
security_group=['testgroup'])
try:
@ -161,7 +162,7 @@ class ComputeTestCase(test.TestCase):
ref = self.compute_api.create(
self.context,
instance_type=FLAGS.default_instance_type,
instance_type=instance_types.get_default_instance_type(),
image_id=None,
security_group=['testgroup'])
try:
@ -177,7 +178,7 @@ class ComputeTestCase(test.TestCase):
ref = self.compute_api.create(
self.context,
instance_type=FLAGS.default_instance_type,
instance_type=instance_types.get_default_instance_type(),
image_id=None,
security_group=['testgroup'])
@ -359,8 +360,9 @@ class ComputeTestCase(test.TestCase):
instance_id = self._create_instance()
self.compute.run_instance(self.context, instance_id)
inst_type = instance_types.get_instance_type_by_name('m1.xlarge')
db.instance_update(self.context, instance_id,
{'instance_type': 'm1.xlarge'})
{'instance_type_id': inst_type['id']})
self.assertRaises(exception.ApiError, self.compute_api.resize,
context, instance_id, 1)
@ -380,8 +382,8 @@ class ComputeTestCase(test.TestCase):
self.compute.terminate_instance(context, instance_id)
def test_get_by_flavor_id(self):
type = instance_types.get_by_flavor_id(1)
self.assertEqual(type, 'm1.tiny')
type = instance_types.get_instance_type_by_flavor_id(1)
self.assertEqual(type['name'], 'm1.tiny')
def test_resize_same_source_fails(self):
"""Ensure instance fails to migrate when source and destination are

View File

@ -62,7 +62,7 @@ class ConsoleTestCase(test.TestCase):
inst['launch_time'] = '10'
inst['user_id'] = self.user.id
inst['project_id'] = self.project.id
inst['instance_type'] = 'm1.tiny'
inst['instance_type_id'] = 1
inst['mac_address'] = utils.generate_mac()
inst['ami_launch_index'] = 0
return db.instance_create(self.context, inst)['id']

View File

@ -40,7 +40,11 @@ class InstanceTypeTestCase(test.TestCase):
max_flavorid = session.query(models.InstanceTypes).\
order_by("flavorid desc").\
first()
max_id = session.query(models.InstanceTypes).\
order_by("id desc").\
first()
self.flavorid = max_flavorid["flavorid"] + 1
self.id = max_id["id"] + 1
self.name = str(int(time.time()))
def test_instance_type_create_then_delete(self):
@ -53,7 +57,7 @@ class InstanceTypeTestCase(test.TestCase):
'instance type was not created')
instance_types.destroy(self.name)
self.assertEqual(1,
instance_types.get_instance_type(self.name)["deleted"])
instance_types.get_instance_type(self.id)["deleted"])
self.assertEqual(starting_inst_list, instance_types.get_all_types())
instance_types.purge(self.name)
self.assertEqual(len(starting_inst_list),

View File

@ -67,7 +67,7 @@ class QuotaTestCase(test.TestCase):
inst['reservation_id'] = 'r-fakeres'
inst['user_id'] = self.user.id
inst['project_id'] = self.project.id
inst['instance_type'] = 'm1.large'
inst['instance_type_id'] = '3' # m1.large
inst['vcpus'] = cores
inst['mac_address'] = utils.generate_mac()
return db.instance_create(self.context, inst)['id']
@ -124,11 +124,12 @@ class QuotaTestCase(test.TestCase):
for i in range(FLAGS.quota_instances):
instance_id = self._create_instance()
instance_ids.append(instance_id)
inst_type = instance_types.get_instance_type_by_name('m1.small')
self.assertRaises(quota.QuotaError, compute.API().create,
self.context,
min_count=1,
max_count=1,
instance_type='m1.small',
instance_type=inst_type,
image_id=1)
for instance_id in instance_ids:
db.instance_destroy(self.context, instance_id)
@ -137,11 +138,12 @@ class QuotaTestCase(test.TestCase):
instance_ids = []
instance_id = self._create_instance(cores=4)
instance_ids.append(instance_id)
inst_type = instance_types.get_instance_type_by_name('m1.small')
self.assertRaises(quota.QuotaError, compute.API().create,
self.context,
min_count=1,
max_count=1,
instance_type='m1.small',
instance_type=inst_type,
image_id=1)
for instance_id in instance_ids:
db.instance_destroy(self.context, instance_id)
@ -192,11 +194,12 @@ class QuotaTestCase(test.TestCase):
metadata = {}
for i in range(FLAGS.quota_metadata_items + 1):
metadata['key%s' % i] = 'value%s' % i
inst_type = instance_types.get_instance_type_by_name('m1.small')
self.assertRaises(quota.QuotaError, compute.API().create,
self.context,
min_count=1,
max_count=1,
instance_type='m1.small',
instance_type=inst_type,
image_id='fake',
metadata=metadata)
@ -207,13 +210,15 @@ class QuotaTestCase(test.TestCase):
def _create_with_injected_files(self, files):
api = compute.API(image_service=self.StubImageService())
inst_type = instance_types.get_instance_type_by_name('m1.small')
api.create(self.context, min_count=1, max_count=1,
instance_type='m1.small', image_id='fake',
instance_type=inst_type, image_id='fake',
injected_files=files)
def test_no_injected_files(self):
api = compute.API(image_service=self.StubImageService())
api.create(self.context, instance_type='m1.small', image_id='fake')
inst_type = instance_types.get_instance_type_by_name('m1.small')
api.create(self.context, instance_type=inst_type, image_id='fake')
def test_max_injected_files(self):
files = []

View File

@ -263,7 +263,7 @@ class SimpleDriverTestCase(test.TestCase):
inst['reservation_id'] = 'r-fakeres'
inst['user_id'] = self.user.id
inst['project_id'] = self.project.id
inst['instance_type'] = 'm1.tiny'
inst['instance_type_id'] = '1'
inst['mac_address'] = utils.generate_mac()
inst['vcpus'] = kwargs.get('vcpus', 1)
inst['ami_launch_index'] = 0

View File

@ -140,7 +140,7 @@ class LibvirtConnTestCase(test.TestCase):
'vcpus': 2,
'project_id': 'fake',
'bridge': 'br101',
'instance_type': 'm1.small'}
'instance_type_id': '5'} # m1.small
def lazy_load_library_exists(self):
"""check if libvirt is available."""
@ -479,7 +479,7 @@ class LibvirtConnTestCase(test.TestCase):
fake_timer = FakeTime()
self.create_fake_libvirt_mock(nwfilterLookupByName=fake_raise)
self.create_fake_libvirt_mock()
instance_ref = db.instance_create(self.context, self.test_instance)
# Start test
@ -488,6 +488,7 @@ class LibvirtConnTestCase(test.TestCase):
conn = libvirt_conn.LibvirtConnection(False)
conn.firewall_driver.setattr('setup_basic_filtering', fake_none)
conn.firewall_driver.setattr('prepare_instance_filter', fake_none)
conn.firewall_driver.setattr('instance_filter_exists', fake_none)
conn.ensure_filtering_rules_for_instance(instance_ref,
time=fake_timer)
except exception.Error, e:

View File

@ -106,7 +106,7 @@ class VolumeTestCase(test.TestCase):
inst['launch_time'] = '10'
inst['user_id'] = 'fake'
inst['project_id'] = 'fake'
inst['instance_type'] = 'm1.tiny'
inst['instance_type_id'] = '2' # m1.tiny
inst['mac_address'] = utils.generate_mac()
inst['ami_launch_index'] = 0
instance_id = db.instance_create(self.context, inst)['id']

View File

@ -80,7 +80,7 @@ class XenAPIVolumeTestCase(test.TestCase):
'image_id': 1,
'kernel_id': 2,
'ramdisk_id': 3,
'instance_type': 'm1.large',
'instance_type_id': '3', # m1.large
'mac_address': 'aa:bb:cc:dd:ee:ff',
'os_type': 'linux'}
@ -289,11 +289,11 @@ class XenAPIVMTestCase(test.TestCase):
'enabled':'1'}],
'ip6s': [{'ip': 'fe80::a8bb:ccff:fedd:eeff',
'netmask': '120',
'enabled': '1',
'gateway': 'fe80::a00:1'}],
'enabled': '1'}],
'mac': 'aa:bb:cc:dd:ee:ff',
'dns': ['10.0.0.2'],
'gateway': '10.0.0.1'})
'gateway': '10.0.0.1',
'gateway6': 'fe80::a00:1'})
def check_vm_params_for_windows(self):
self.assertEquals(self.vm['platform']['nx'], 'true')
@ -328,7 +328,7 @@ class XenAPIVMTestCase(test.TestCase):
self.assertEquals(self.vm['HVM_boot_policy'], '')
def _test_spawn(self, image_id, kernel_id, ramdisk_id,
instance_type="m1.large", os_type="linux",
instance_type_id="3", os_type="linux",
instance_id=1, check_injection=False):
stubs.stubout_loopingcall_start(self.stubs)
values = {'id': instance_id,
@ -337,7 +337,7 @@ class XenAPIVMTestCase(test.TestCase):
'image_id': image_id,
'kernel_id': kernel_id,
'ramdisk_id': ramdisk_id,
'instance_type': instance_type,
'instance_type_id': instance_type_id,
'mac_address': 'aa:bb:cc:dd:ee:ff',
'os_type': os_type}
instance = db.instance_create(self.context, values)
@ -349,7 +349,7 @@ class XenAPIVMTestCase(test.TestCase):
FLAGS.xenapi_image_service = 'glance'
self.assertRaises(Exception,
self._test_spawn,
1, 2, 3, "m1.xlarge")
1, 2, 3, "4") # m1.xlarge
def test_spawn_raw_objectstore(self):
FLAGS.xenapi_image_service = 'objectstore'
@ -523,7 +523,7 @@ class XenAPIVMTestCase(test.TestCase):
'image_id': 1,
'kernel_id': 2,
'ramdisk_id': 3,
'instance_type': 'm1.large',
'instance_type_id': '3', # m1.large
'mac_address': 'aa:bb:cc:dd:ee:ff',
'os_type': 'linux'}
instance = db.instance_create(self.context, values)
@ -580,7 +580,7 @@ class XenAPIMigrateInstance(test.TestCase):
'kernel_id': None,
'ramdisk_id': None,
'local_gb': 5,
'instance_type': 'm1.large',
'instance_type_id': '3', # m1.large
'mac_address': 'aa:bb:cc:dd:ee:ff',
'os_type': 'linux'}

View File

@ -169,34 +169,34 @@ def _get_network_info(instance):
instance['id'])
network_info = []
def ip_dict(ip):
return {
"ip": ip.address,
"netmask": network["netmask"],
"enabled": "1"}
def ip6_dict(ip6):
prefix = ip6.network.cidr_v6
mac = instance.mac_address
return {
"ip": utils.to_global_ipv6(prefix, mac),
"netmask": ip6.network.netmask_v6,
"gateway": ip6.network.gateway_v6,
"enabled": "1"}
for network in networks:
network_ips = [ip for ip in ip_addresses
if ip.network_id == network.id]
if ip['network_id'] == network['id']]
def ip_dict(ip):
return {
'ip': ip['address'],
'netmask': network['netmask'],
'enabled': '1'}
def ip6_dict():
prefix = network['cidr_v6']
mac = instance['mac_address']
return {
'ip': utils.to_global_ipv6(prefix, mac),
'netmask': network['netmask_v6'],
'enabled': '1'}
mapping = {
'label': network['label'],
'gateway': network['gateway'],
'mac': instance.mac_address,
'mac': instance['mac_address'],
'dns': [network['dns']],
'ips': [ip_dict(ip) for ip in network_ips]}
if FLAGS.use_ipv6:
mapping['ip6s'] = [ip6_dict(ip) for ip in network_ips]
mapping['ip6s'] = [ip6_dict()]
mapping['gateway6'] = network['gateway_v6']
network_info.append((network, mapping))
return network_info
@ -796,7 +796,10 @@ class LibvirtConnection(driver.ComputeDriver):
root_fname = '%08x' % int(disk_images['image_id'])
size = FLAGS.minimum_root_size
if inst['instance_type'] == 'm1.tiny' or suffix == '.rescue':
inst_type_id = inst['instance_type_id']
inst_type = instance_types.get_instance_type(inst_type_id)
if inst_type['name'] == 'm1.tiny' or suffix == '.rescue':
size = None
root_fname += "_sm"
@ -808,14 +811,13 @@ class LibvirtConnection(driver.ComputeDriver):
user=user,
project=project,
size=size)
type_data = instance_types.get_instance_type(inst['instance_type'])
if type_data['local_gb']:
if inst_type['local_gb']:
self._cache_image(fn=self._create_local,
target=basepath('disk.local'),
fname="local_%s" % type_data['local_gb'],
fname="local_%s" % inst_type['local_gb'],
cow=FLAGS.use_cow_images,
local_gb=type_data['local_gb'])
local_gb=inst_type['local_gb'])
# For now, we assume that if we're not using a kernel, we're using a
# partitioned disk image where the target partition is the first
@ -949,8 +951,8 @@ class LibvirtConnection(driver.ComputeDriver):
nics.append(self._get_nic_for_xml(network,
mapping))
# FIXME(vish): stick this in db
instance_type_name = instance['instance_type']
instance_type = instance_types.get_instance_type(instance_type_name)
inst_type_id = instance['instance_type_id']
inst_type = instance_types.get_instance_type(inst_type_id)
if FLAGS.use_cow_images:
driver_type = 'qcow2'
@ -961,10 +963,10 @@ class LibvirtConnection(driver.ComputeDriver):
'name': instance['name'],
'basepath': os.path.join(FLAGS.instances_path,
instance['name']),
'memory_kb': instance_type['memory_mb'] * 1024,
'vcpus': instance_type['vcpus'],
'memory_kb': inst_type['memory_mb'] * 1024,
'vcpus': inst_type['vcpus'],
'rescue': rescue,
'local': instance_type['local_gb'],
'local': inst_type['local_gb'],
'driver_type': driver_type,
'nics': nics}
@ -1401,18 +1403,13 @@ class LibvirtConnection(driver.ComputeDriver):
# wait for completion
timeout_count = range(FLAGS.live_migration_retry_count)
while timeout_count:
try:
filter_name = 'nova-instance-%s' % instance_ref.name
self._conn.nwfilterLookupByName(filter_name)
if self.firewall_driver.instance_filter_exists(instance_ref):
break
except libvirt.libvirtError:
timeout_count.pop()
if len(timeout_count) == 0:
ec2_id = instance_ref['hostname']
iname = instance_ref.name
msg = _('Timeout migrating for %(ec2_id)s(%(iname)s)')
raise exception.Error(msg % locals())
time.sleep(1)
timeout_count.pop()
if len(timeout_count) == 0:
msg = _('Timeout migrating for %s. nwfilter not found.')
raise exception.Error(msg % instance_ref.name)
time.sleep(1)
def live_migration(self, ctxt, instance_ref, dest,
post_method, recover_method):
@ -1541,6 +1538,10 @@ class FirewallDriver(object):
"""
raise NotImplementedError()
def instance_filter_exists(self, instance):
"""Check nova-instance-instance-xxx exists"""
raise NotImplementedError()
class NWFilterFirewall(FirewallDriver):
"""
@ -1848,6 +1849,21 @@ class NWFilterFirewall(FirewallDriver):
return 'nova-instance-%s' % (instance['name'])
return 'nova-instance-%s-%s' % (instance['name'], nic_id)
def instance_filter_exists(self, instance):
"""Check nova-instance-instance-xxx exists"""
network_info = _get_network_info(instance)
for (network, mapping) in network_info:
nic_id = mapping['mac'].replace(':', '')
instance_filter_name = self._instance_filter_name(instance, nic_id)
try:
self._conn.nwfilterLookupByName(instance_filter_name)
except libvirt.libvirtError:
name = instance.name
LOG.debug(_('The nwfilter(%(instance_filter_name)s) for'
'%(name)s is not found.') % locals())
return False
return True
class IptablesFirewallDriver(FirewallDriver):
def __init__(self, execute=None, **kwargs):
@ -2037,6 +2053,10 @@ class IptablesFirewallDriver(FirewallDriver):
return ipv4_rules, ipv6_rules
def instance_filter_exists(self, instance):
"""Check nova-instance-instance-xxx exists"""
return self.nwfilter.instance_filter_exists(instance)
def refresh_security_group_members(self, security_group):
pass

View File

@ -101,8 +101,8 @@ class VMHelper(HelperBase):
3. Using hardware virtualization
"""
instance_type = instance_types.\
get_instance_type(instance.instance_type)
inst_type_id = instance.instance_type_id
instance_type = instance_types.get_instance_type(inst_type_id)
mem = str(long(instance_type['memory_mb']) * 1024 * 1024)
vcpus = str(instance_type['vcpus'])
rec = {
@ -169,8 +169,8 @@ class VMHelper(HelperBase):
@classmethod
def ensure_free_mem(cls, session, instance):
instance_type = instance_types.get_instance_type(
instance.instance_type)
inst_type_id = instance.instance_type_id
instance_type = instance_types.get_instance_type(inst_type_id)
mem = long(instance_type['memory_mb']) * 1024 * 1024
#get free memory from host
host = session.get_xenapi_host()
@ -1130,7 +1130,7 @@ def _prepare_injectables(inst, networks_info):
'dns': dns,
'address_v6': ip_v6 and ip_v6['ip'] or '',
'netmask_v6': ip_v6 and ip_v6['netmask'] or '',
'gateway_v6': ip_v6 and ip_v6['gateway'] or '',
'gateway_v6': ip_v6 and info['gateway6'] or '',
'use_ipv6': FLAGS.use_ipv6}
interfaces_info.append(interface_info)

View File

@ -176,7 +176,7 @@ class VMOps(object):
vdi_ref, network_info)
self.create_vifs(vm_ref, network_info)
self.inject_network_info(instance, vm_ref, network_info)
self.inject_network_info(instance, network_info, vm_ref)
return vm_ref
def _spawn(self, instance, vm_ref):
@ -802,8 +802,10 @@ class VMOps(object):
instance['id'])
networks = db.network_get_all_by_instance(admin_context,
instance['id'])
flavor = db.instance_type_get_by_name(admin_context,
instance['instance_type'])
inst_type = db.instance_type_get_by_id(admin_context,
instance['instance_type_id'])
network_info = []
for network in networks:
network_IPs = [ip for ip in IPs if ip.network_id == network.id]
@ -814,12 +816,11 @@ class VMOps(object):
"netmask": network["netmask"],
"enabled": "1"}
def ip6_dict(ip6):
def ip6_dict():
return {
"ip": utils.to_global_ipv6(network['cidr_v6'],
instance['mac_address']),
"netmask": network['netmask_v6'],
"gateway": network['gateway_v6'],
"enabled": "1"}
info = {
@ -827,23 +828,41 @@ class VMOps(object):
'gateway': network['gateway'],
'broadcast': network['broadcast'],
'mac': instance.mac_address,
'rxtx_cap': flavor['rxtx_cap'],
'rxtx_cap': inst_type['rxtx_cap'],
'dns': [network['dns']],
'ips': [ip_dict(ip) for ip in network_IPs]}
if network['cidr_v6']:
info['ip6s'] = [ip6_dict(ip) for ip in network_IPs]
info['ip6s'] = [ip6_dict()]
if network['gateway_v6']:
info['gateway6'] = network['gateway_v6']
network_info.append((network, info))
return network_info
def inject_network_info(self, instance, vm_ref, network_info):
#TODO{tr3buchet) remove this shim with nova-multi-nic
def inject_network_info(self, instance, network_info=None, vm_ref=None):
"""
shim in place which makes inject_network_info work without being
passed network_info.
shim goes away after nova-multi-nic
"""
if not network_info:
network_info = self._get_network_info(instance)
self._inject_network_info(instance, network_info, vm_ref)
def _inject_network_info(self, instance, network_info, vm_ref=None):
"""
Generate the network info and make calls to place it into the
xenstore and the xenstore param list.
vm_ref can be passed in because it will sometimes be different than
what VMHelper.lookup(session, instance.name) will find (ex: rescue)
"""
logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref)
# this function raises if vm_ref is not a vm_opaque_ref
self._session.get_xenapi().VM.get_record(vm_ref)
if vm_ref:
# this function raises if vm_ref is not a vm_opaque_ref
self._session.get_xenapi().VM.get_record(vm_ref)
else:
vm_ref = VMHelper.lookup(self._session, instance.name)
for (network, info) in network_info:
location = 'vm-data/networking/%s' % info['mac'].replace(':', '')
@ -875,8 +894,10 @@ class VMOps(object):
VMHelper.create_vif(self._session, vm_ref, network_ref,
mac_address, device, rxtx_cap)
def reset_network(self, instance, vm_ref):
def reset_network(self, instance, vm_ref=None):
"""Creates uuid arg to pass to make_agent_call and calls it."""
if not vm_ref:
vm_ref = VMHelper.lookup(self._session, instance.name)
args = {'id': str(uuid.uuid4())}
# TODO(tr3buchet): fix function call after refactor
#resp = self._make_agent_call('resetnetwork', instance, '', args)

View File

@ -63,6 +63,7 @@ import xmlrpclib
from eventlet import event
from eventlet import tpool
from eventlet import timeout
from nova import context
from nova import db
@ -140,6 +141,9 @@ flags.DEFINE_bool('xenapi_remap_vbd_dev', False,
flags.DEFINE_string('xenapi_remap_vbd_dev_prefix', 'sd',
'Specify prefix to remap VBD dev to '
'(ex. /dev/xvdb -> /dev/sdb)')
flags.DEFINE_integer('xenapi_login_timeout',
10,
'Timeout in seconds for XenAPI login.')
def get_connection(_):
@ -318,7 +322,10 @@ class XenAPISession(object):
def __init__(self, url, user, pw):
self.XenAPI = self.get_imported_xenapi()
self._session = self._create_session(url)
self._session.login_with_password(user, pw)
exception = self.XenAPI.Failure(_("Unable to log in to XenAPI "
"(is the Dom0 disk full?)"))
with timeout.Timeout(FLAGS.xenapi_login_timeout, exception):
self._session.login_with_password(user, pw)
self.loop = None
def get_imported_xenapi(self):

View File

@ -355,24 +355,25 @@ class Controller(object):
if type(result) is dict:
content_type = req.best_match_content_type()
body = self._serialize(result, content_type)
default_xmlns = self.get_default_xmlns(req)
body = self._serialize(result, content_type, default_xmlns)
response = webob.Response()
response.headers["Content-Type"] = content_type
response.body = body
return response
else:
return result
def _serialize(self, data, content_type):
def _serialize(self, data, content_type, default_xmlns):
"""
Serialize the given dict to the provided content_type.
Uses self._serialization_metadata if it exists, which is a dict mapping
MIME types to information needed to serialize to that type.
"""
_metadata = getattr(type(self), "_serialization_metadata", {})
serializer = Serializer(_metadata)
serializer = Serializer(_metadata, default_xmlns)
try:
return serializer.serialize(data, content_type)
except exception.InvalidContentType:
@ -388,19 +389,24 @@ class Controller(object):
serializer = Serializer(_metadata)
return serializer.deserialize(data, content_type)
def get_default_xmlns(self, req):
"""Provide the XML namespace to use if none is otherwise specified."""
return None
class Serializer(object):
"""
Serializes and deserializes dictionaries to certain MIME types.
"""
def __init__(self, metadata=None):
def __init__(self, metadata=None, default_xmlns=None):
"""
Create a serializer based on the given WSGI environment.
'metadata' is an optional dict mapping MIME types to information
needed to serialize a dictionary to that type.
"""
self.metadata = metadata or {}
self.default_xmlns = default_xmlns
def _get_serialize_handler(self, content_type):
handlers = {
@ -478,11 +484,23 @@ class Serializer(object):
root_key = data.keys()[0]
doc = minidom.Document()
node = self._to_xml_node(doc, metadata, root_key, data[root_key])
xmlns = node.getAttribute('xmlns')
if not xmlns and self.default_xmlns:
node.setAttribute('xmlns', self.default_xmlns)
return node.toprettyxml(indent=' ')
def _to_xml_node(self, doc, metadata, nodename, data):
"""Recursive method to convert data members to XML nodes."""
result = doc.createElement(nodename)
# Set the xml namespace if one is specified
# TODO(justinsb): We could also use prefixes on the keys
xmlns = metadata.get('xmlns', None)
if xmlns:
result.setAttribute('xmlns', xmlns)
if type(data) is list:
singular = metadata.get('plurals', {}).get(nodename, None)
if singular is None:
@ -532,6 +550,7 @@ def paste_config_file(basename):
"""
configfiles = [basename,
os.path.join(FLAGS.state_path, 'etc', 'nova', basename),
os.path.join(FLAGS.state_path, 'etc', basename),
os.path.join(FLAGS.state_path, basename),
'/etc/nova/%s' % basename]

View File

@ -56,16 +56,17 @@ def read_record(self, arg_dict):
and boolean True, attempting to read a non-existent path will return
the string 'None' instead of raising an exception.
"""
cmd = "xenstore-read /local/domain/%(dom_id)s/%(path)s" % arg_dict
cmd = ["xenstore-read", "/local/domain/%(dom_id)s/%(path)s" % arg_dict]
try:
return _run_command(cmd).rstrip("\n")
ret, result = _run_command(cmd)
return result.rstrip("\n")
except pluginlib.PluginError, e:
if arg_dict.get("ignore_missing_path", False):
cmd = "xenstore-exists /local/domain/%(dom_id)s/%(path)s; echo $?"
cmd = cmd % arg_dict
ret = _run_command(cmd).strip()
cmd = ["xenstore-exists",
"/local/domain/%(dom_id)s/%(path)s" % arg_dict]
ret, result = _run_command(cmd).strip()
# If the path exists, the cmd should return "0"
if ret != "0":
if ret != 0:
# No such path, so ignore the error and return the
# string 'None', since None can't be marshalled
# over RPC.
@ -83,8 +84,9 @@ def write_record(self, arg_dict):
you must specify a 'value' key, whose value must be a string. Typically,
you can json-ify more complex values and store the json output.
"""
cmd = "xenstore-write /local/domain/%(dom_id)s/%(path)s '%(value)s'"
cmd = cmd % arg_dict
cmd = ["xenstore-write",
"/local/domain/%(dom_id)s/%(path)s" % arg_dict,
arg_dict["value"]]
_run_command(cmd)
return arg_dict["value"]
@ -96,10 +98,10 @@ def list_records(self, arg_dict):
path as the key and the stored value as the value. If the path
doesn't exist, an empty dict is returned.
"""
cmd = "xenstore-ls /local/domain/%(dom_id)s/%(path)s" % arg_dict
cmd = cmd.rstrip("/")
dirpath = "/local/domain/%(dom_id)s/%(path)s" % arg_dict
cmd = ["xenstore-ls", dirpath.rstrip("/")]
try:
recs = _run_command(cmd)
ret, recs = _run_command(cmd)
except pluginlib.PluginError, e:
if "No such file or directory" in "%s" % e:
# Path doesn't exist.
@ -128,8 +130,9 @@ def delete_record(self, arg_dict):
"""Just like it sounds: it removes the record for the specified
VM and the specified path from xenstore.
"""
cmd = "xenstore-rm /local/domain/%(dom_id)s/%(path)s" % arg_dict
return _run_command(cmd)
cmd = ["xenstore-rm", "/local/domain/%(dom_id)s/%(path)s" % arg_dict]
ret, result = _run_command(cmd)
return result
def _paths_from_ls(recs):
@ -171,9 +174,9 @@ def _run_command(cmd):
Otherwise, the output from stdout is returned.
"""
pipe = subprocess.PIPE
proc = subprocess.Popen([cmd], shell=True, stdin=pipe, stdout=pipe,
stderr=pipe, close_fds=True)
proc.wait()
proc = subprocess.Popen(cmd, stdin=pipe, stdout=pipe, stderr=pipe,
close_fds=True)
ret = proc.wait()
err = proc.stderr.read()
if err:
raise pluginlib.PluginError(err)

View File

@ -16,6 +16,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import glob
import os
import subprocess
import sys
@ -86,6 +87,19 @@ try:
except:
pass
def find_data_files(destdir, srcdir):
package_data = []
files = []
for d in glob.glob('%s/*' % (srcdir, )):
if os.path.isdir(d):
package_data += find_data_files(
os.path.join(destdir, os.path.basename(d)), d)
else:
files += [d]
package_data += [(destdir, files)]
return package_data
DistUtilsExtra.auto.setup(name='nova',
version=version.canonical_version_string(),
description='cloud computing fabric controller',
@ -96,6 +110,7 @@ DistUtilsExtra.auto.setup(name='nova',
packages=find_packages(exclude=['bin', 'smoketests']),
include_package_data=True,
test_suite='nose.collector',
data_files=find_data_files('share/nova', 'tools'),
scripts=['bin/nova-ajax-console-proxy',
'bin/nova-api',
'bin/nova-compute',