merging trunk

This commit is contained in:
Brian Waldon 2011-06-13 12:54:43 -04:00
commit 7c68bb8172
49 changed files with 833 additions and 256 deletions

View File

@ -31,6 +31,7 @@ Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp>
Hisaki Ohara <hisaki.ohara@intel.com> Hisaki Ohara <hisaki.ohara@intel.com>
Ilya Alekseyev <ialekseev@griddynamics.com> Ilya Alekseyev <ialekseev@griddynamics.com>
Isaku Yamahata <yamahata@valinux.co.jp> Isaku Yamahata <yamahata@valinux.co.jp>
Jason Cannavale <jason.cannavale@rackspace.com>
Jason Koelker <jason@koelker.net> Jason Koelker <jason@koelker.net>
Jay Pipes <jaypipes@gmail.com> Jay Pipes <jaypipes@gmail.com>
Jesse Andrews <anotherjesse@gmail.com> Jesse Andrews <anotherjesse@gmail.com>

View File

@ -96,6 +96,7 @@ flags.DECLARE('network_size', 'nova.network.manager')
flags.DECLARE('vlan_start', 'nova.network.manager') flags.DECLARE('vlan_start', 'nova.network.manager')
flags.DECLARE('vpn_start', 'nova.network.manager') flags.DECLARE('vpn_start', 'nova.network.manager')
flags.DECLARE('fixed_range_v6', 'nova.network.manager') flags.DECLARE('fixed_range_v6', 'nova.network.manager')
flags.DECLARE('gateway_v6', 'nova.network.manager')
flags.DECLARE('libvirt_type', 'nova.virt.libvirt.connection') flags.DECLARE('libvirt_type', 'nova.virt.libvirt.connection')
flags.DEFINE_flag(flags.HelpFlag()) flags.DEFINE_flag(flags.HelpFlag())
flags.DEFINE_flag(flags.HelpshortFlag()) flags.DEFINE_flag(flags.HelpshortFlag())
@ -544,13 +545,10 @@ class FloatingIpCommands(object):
class NetworkCommands(object): class NetworkCommands(object):
"""Class for managing networks.""" """Class for managing networks."""
def create(self, fixed_range=None, num_networks=None, def create(self, fixed_range=None, num_networks=None, network_size=None,
network_size=None, vlan_start=None, vlan_start=None, vpn_start=None, fixed_range_v6=None,
vpn_start=None, fixed_range_v6=None, label='public'): gateway_v6=None, label='public'):
"""Creates fixed ips for host by range """Creates fixed ips for host by range"""
arguments: fixed_range=FLAG, [num_networks=FLAG],
[network_size=FLAG], [vlan_start=FLAG],
[vpn_start=FLAG], [fixed_range_v6=FLAG]"""
if not fixed_range: if not fixed_range:
msg = _('Fixed range in the form of 10.0.0.0/8 is ' msg = _('Fixed range in the form of 10.0.0.0/8 is '
'required to create networks.') 'required to create networks.')
@ -566,6 +564,8 @@ class NetworkCommands(object):
vpn_start = FLAGS.vpn_start vpn_start = FLAGS.vpn_start
if not fixed_range_v6: if not fixed_range_v6:
fixed_range_v6 = FLAGS.fixed_range_v6 fixed_range_v6 = FLAGS.fixed_range_v6
if not gateway_v6:
gateway_v6 = FLAGS.gateway_v6
net_manager = utils.import_object(FLAGS.network_manager) net_manager = utils.import_object(FLAGS.network_manager)
try: try:
net_manager.create_networks(context.get_admin_context(), net_manager.create_networks(context.get_admin_context(),
@ -575,6 +575,7 @@ class NetworkCommands(object):
vlan_start=int(vlan_start), vlan_start=int(vlan_start),
vpn_start=int(vpn_start), vpn_start=int(vpn_start),
cidr_v6=fixed_range_v6, cidr_v6=fixed_range_v6,
gateway_v6=gateway_v6,
label=label) label=label)
except ValueError, e: except ValueError, e:
print e print e

View File

@ -324,7 +324,7 @@ class Limited(object):
def __init__(self, proxy): def __init__(self, proxy):
self._proxy = proxy self._proxy = proxy
if not self.__doc__: if not self.__doc__: # pylint: disable=E0203
self.__doc__ = proxy.__doc__ self.__doc__ = proxy.__doc__
if not self._allowed: if not self._allowed:
self._allowed = [] self._allowed = []

View File

@ -242,6 +242,7 @@ class Authorizer(wsgi.Middleware):
'CreateKeyPair': ['all'], 'CreateKeyPair': ['all'],
'DeleteKeyPair': ['all'], 'DeleteKeyPair': ['all'],
'DescribeSecurityGroups': ['all'], 'DescribeSecurityGroups': ['all'],
'ImportPublicKey': ['all'],
'AuthorizeSecurityGroupIngress': ['netadmin'], 'AuthorizeSecurityGroupIngress': ['netadmin'],
'RevokeSecurityGroupIngress': ['netadmin'], 'RevokeSecurityGroupIngress': ['netadmin'],
'CreateSecurityGroup': ['netadmin'], 'CreateSecurityGroup': ['netadmin'],

View File

@ -324,7 +324,3 @@ class AdminController(object):
rv.append(host_dict(host, compute, instances, volume, volumes, rv.append(host_dict(host, compute, instances, volume, volumes,
now)) now))
return {'hosts': rv} return {'hosts': rv}
def describe_host(self, _context, name, **_kwargs):
"""Returns status info for single node."""
return host_dict(db.host_get(name))

View File

@ -39,6 +39,7 @@ from nova import flags
from nova import ipv6 from nova import ipv6
from nova import log as logging from nova import log as logging
from nova import network from nova import network
from nova import rpc
from nova import utils from nova import utils
from nova import volume from nova import volume
from nova.api.ec2 import ec2utils from nova.api.ec2 import ec2utils
@ -136,6 +137,13 @@ class CloudController(object):
return services[0]['availability_zone'] return services[0]['availability_zone']
return 'unknown zone' return 'unknown zone'
def _get_image_state(self, image):
# NOTE(vish): fallback status if image_state isn't set
state = image.get('status')
if state == 'active':
state = 'available'
return image['properties'].get('image_state', state)
def get_metadata(self, address): def get_metadata(self, address):
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
instance_ref = self.compute_api.get_all(ctxt, fixed_ip=address) instance_ref = self.compute_api.get_all(ctxt, fixed_ip=address)
@ -865,8 +873,14 @@ class CloudController(object):
def allocate_address(self, context, **kwargs): def allocate_address(self, context, **kwargs):
LOG.audit(_("Allocate address"), context=context) LOG.audit(_("Allocate address"), context=context)
public_ip = self.network_api.allocate_floating_ip(context) try:
return {'publicIp': public_ip} public_ip = self.network_api.allocate_floating_ip(context)
return {'publicIp': public_ip}
except rpc.RemoteError as ex:
if ex.exc_type == 'NoMoreAddresses':
raise exception.NoMoreFloatingIps()
else:
raise
def release_address(self, context, public_ip, **kwargs): def release_address(self, context, public_ip, **kwargs):
LOG.audit(_("Release address %s"), public_ip, context=context) LOG.audit(_("Release address %s"), public_ip, context=context)
@ -896,14 +910,13 @@ class CloudController(object):
ramdisk = self._get_image(context, kwargs['ramdisk_id']) ramdisk = self._get_image(context, kwargs['ramdisk_id'])
kwargs['ramdisk_id'] = ramdisk['id'] kwargs['ramdisk_id'] = ramdisk['id']
image = self._get_image(context, kwargs['image_id']) image = self._get_image(context, kwargs['image_id'])
if not image:
raise exception.ImageNotFound(image_id=kwargs['image_id'])
try:
available = (image['properties']['image_state'] == 'available')
except KeyError:
available = False
if not available: if image:
image_state = self._get_image_state(image)
else:
raise exception.ImageNotFound(image_id=kwargs['image_id'])
if image_state != 'available':
raise exception.ApiError(_('Image must be available')) raise exception.ApiError(_('Image must be available'))
instances = self.compute_api.create(context, instances = self.compute_api.create(context,
@ -1021,11 +1034,8 @@ class CloudController(object):
get('image_location'), name) get('image_location'), name)
else: else:
i['imageLocation'] = image['properties'].get('image_location') i['imageLocation'] = image['properties'].get('image_location')
# NOTE(vish): fallback status if image_state isn't set
state = image.get('status') i['imageState'] = self._get_image_state(image)
if state == 'active':
state = 'available'
i['imageState'] = image['properties'].get('image_state', state)
i['displayName'] = name i['displayName'] = name
i['description'] = image.get('description') i['description'] = image.get('description')
display_mapping = {'aki': 'kernel', display_mapping = {'aki': 'kernel',

View File

@ -101,7 +101,7 @@ class APIRouter(base_wsgi.Router):
mapper.resource("zone", "zones", mapper.resource("zone", "zones",
controller=zones.create_resource(), controller=zones.create_resource(),
collection={'detail': 'GET', 'info': 'GET', collection={'detail': 'GET', 'info': 'GET',
'select': 'GET'}) 'select': 'POST'})
mapper.resource("user", "users", mapper.resource("user", "users",
controller=users.create_resource(), controller=users.create_resource(),

View File

@ -49,19 +49,22 @@ class AuthMiddleware(wsgi.Middleware):
if not self.has_authentication(req): if not self.has_authentication(req):
return self.authenticate(req) return self.authenticate(req)
user = self.get_user_by_authentication(req) user = self.get_user_by_authentication(req)
accounts = self.auth.get_projects(user=user)
if not user: if not user:
token = req.headers["X-Auth-Token"] token = req.headers["X-Auth-Token"]
msg = _("%(user)s could not be found with token '%(token)s'") msg = _("%(user)s could not be found with token '%(token)s'")
LOG.warn(msg % locals()) LOG.warn(msg % locals())
return faults.Fault(webob.exc.HTTPUnauthorized()) return faults.Fault(webob.exc.HTTPUnauthorized())
if accounts: try:
#we are punting on this til auth is settled, account = req.headers["X-Auth-Project-Id"]
#and possibly til api v1.1 (mdragon) except KeyError:
account = accounts[0] # FIXME(usrleon): It needed only for compatibility
else: # while osapi clients don't use this header
return faults.Fault(webob.exc.HTTPUnauthorized()) accounts = self.auth.get_projects(user=user)
if accounts:
account = accounts[0]
else:
return faults.Fault(webob.exc.HTTPUnauthorized())
if not self.auth.is_admin(user) and \ if not self.auth.is_admin(user) and \
not self.auth.is_project_member(user, account): not self.auth.is_project_member(user, account):

View File

@ -137,7 +137,7 @@ class ActionExtensionResource(wsgi.Resource):
def __init__(self, application): def __init__(self, application):
controller = ActionExtensionController(application) controller = ActionExtensionController(application)
super(ActionExtensionResource, self).__init__(controller) wsgi.Resource.__init__(self, controller)
def add_action(self, action_name, handler): def add_action(self, action_name, handler):
self.controller.add_action(action_name, handler) self.controller.add_action(action_name, handler)
@ -164,7 +164,7 @@ class RequestExtensionResource(wsgi.Resource):
def __init__(self, application): def __init__(self, application):
controller = RequestExtensionController(application) controller = RequestExtensionController(application)
super(RequestExtensionResource, self).__init__(controller) wsgi.Resource.__init__(self, controller)
def add_handler(self, handler): def add_handler(self, handler):
self.controller.add_handler(handler) self.controller.add_handler(handler)

View File

@ -153,6 +153,7 @@ class Controller(object):
msg = _("Server name is not defined") msg = _("Server name is not defined")
return exc.HTTPBadRequest(msg) return exc.HTTPBadRequest(msg)
zone_blob = body['server'].get('blob')
name = body['server']['name'] name = body['server']['name']
self._validate_server_name(name) self._validate_server_name(name)
name = name.strip() name = name.strip()
@ -172,7 +173,8 @@ class Controller(object):
key_data=key_data, key_data=key_data,
metadata=body['server'].get('metadata', {}), metadata=body['server'].get('metadata', {}),
injected_files=injected_files, injected_files=injected_files,
admin_password=password) admin_password=password,
zone_blob=zone_blob)
except quota.QuotaError as error: except quota.QuotaError as error:
self._handle_quota_error(error) self._handle_quota_error(error)
except exception.ImageNotFound as error: except exception.ImageNotFound as error:

View File

@ -35,7 +35,7 @@ class Versions(wsgi.Resource):
'application/xml': wsgi.XMLDictSerializer(metadata=metadata), 'application/xml': wsgi.XMLDictSerializer(metadata=metadata),
} }
super(Versions, self).__init__(None, serializers=serializers) wsgi.Resource.__init__(self, None, serializers=serializers)
def dispatch(self, request, *args): def dispatch(self, request, *args):
"""Respond to a request for all OpenStack API versions.""" """Respond to a request for all OpenStack API versions."""

View File

@ -29,9 +29,6 @@ class ViewBuilder(object):
def _build_rate_limit(self, rate_limit): def _build_rate_limit(self, rate_limit):
raise NotImplementedError() raise NotImplementedError()
def _build_absolute_limits(self, absolute_limit):
raise NotImplementedError()
def build(self, rate_limits, absolute_limits): def build(self, rate_limits, absolute_limits):
rate_limits = self._build_rate_limits(rate_limits) rate_limits = self._build_rate_limits(rate_limits)
absolute_limits = self._build_absolute_limits(absolute_limits) absolute_limits = self._build_absolute_limits(absolute_limits)
@ -67,12 +64,6 @@ class ViewBuilder(object):
limits[name] = value limits[name] = value
return limits return limits
def _build_rate_limits(self, rate_limits):
raise NotImplementedError()
def _build_rate_limit(self, rate_limit):
raise NotImplementedError()
class ViewBuilderV10(ViewBuilder): class ViewBuilderV10(ViewBuilder):
"""Openstack API v1.0 limits view builder.""" """Openstack API v1.0 limits view builder."""

View File

@ -225,7 +225,7 @@ class XMLDictSerializer(DictSerializer):
if not xmlns and self.xmlns: if not xmlns and self.xmlns:
node.setAttribute('xmlns', self.xmlns) node.setAttribute('xmlns', self.xmlns)
return node.toprettyxml(indent=' ') return node.toprettyxml(indent=' ', encoding='utf-8')
def _to_xml_node(self, doc, metadata, nodename, data): def _to_xml_node(self, doc, metadata, nodename, data):
"""Recursive method to convert data members to XML nodes.""" """Recursive method to convert data members to XML nodes."""

View File

@ -27,9 +27,6 @@ from nova.scheduler import api
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
flags.DEFINE_string('build_plan_encryption_key',
None,
'128bit (hex) encryption key for scheduler build plans.')
LOG = logging.getLogger('nova.api.openstack.zones') LOG = logging.getLogger('nova.api.openstack.zones')
@ -53,6 +50,14 @@ def _scrub_zone(zone):
'deleted', 'deleted_at', 'updated_at')) 'deleted', 'deleted_at', 'updated_at'))
def check_encryption_key(func):
def wrapped(*args, **kwargs):
if not FLAGS.build_plan_encryption_key:
raise exception.Error(_("--build_plan_encryption_key not set"))
return func(*args, **kwargs)
return wrapped
class Controller(object): class Controller(object):
def index(self, req): def index(self, req):
@ -103,19 +108,13 @@ class Controller(object):
zone = api.zone_update(context, zone_id, body["zone"]) zone = api.zone_update(context, zone_id, body["zone"])
return dict(zone=_scrub_zone(zone)) return dict(zone=_scrub_zone(zone))
def select(self, req): @check_encryption_key
def select(self, req, body):
"""Returns a weighted list of costs to create instances """Returns a weighted list of costs to create instances
of desired capabilities.""" of desired capabilities."""
ctx = req.environ['nova.context'] ctx = req.environ['nova.context']
qs = req.environ['QUERY_STRING'] specs = json.loads(body)
param_dict = urlparse.parse_qs(qs) build_plan = api.select(ctx, specs=specs)
param_dict.pop("fresh", None)
# parse_qs returns a dict where the values are lists,
# since query strings can have multiple values for the
# same key. We need to convert that to single values.
for key in param_dict:
param_dict[key] = param_dict[key][0]
build_plan = api.select(ctx, specs=param_dict)
cooked = self._scrub_build_plan(build_plan) cooked = self._scrub_build_plan(build_plan)
return {"weights": cooked} return {"weights": cooked}
@ -123,9 +122,6 @@ class Controller(object):
"""Remove all the confidential data and return a sanitized """Remove all the confidential data and return a sanitized
version of the build plan. Include an encrypted full version version of the build plan. Include an encrypted full version
of the weighting entry so we can get back to it later.""" of the weighting entry so we can get back to it later."""
if not FLAGS.build_plan_encryption_key:
raise exception.FlagNotSet(flag='build_plan_encryption_key')
encryptor = crypto.encryptor(FLAGS.build_plan_encryption_key) encryptor = crypto.encryptor(FLAGS.build_plan_encryption_key)
cooked = [] cooked = []
for entry in build_plan: for entry in build_plan:

View File

@ -139,7 +139,7 @@ class LdapDriver(object):
self.__cache = None self.__cache = None
return False return False
def __local_cache(key_fmt): def __local_cache(key_fmt): # pylint: disable=E0213
"""Wrap function to cache it's result in self.__cache. """Wrap function to cache it's result in self.__cache.
Works only with functions with one fixed argument. Works only with functions with one fixed argument.
""" """

View File

@ -14,4 +14,5 @@ alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_P
alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --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_API_KEY="%(access)s"
export NOVA_USERNAME="%(user)s" export NOVA_USERNAME="%(user)s"
export NOVA_PROJECT_ID="%(project)s"
export NOVA_URL="%(os)s" export NOVA_URL="%(os)s"

View File

@ -128,18 +128,16 @@ class API(base.Base):
LOG.warn(msg) LOG.warn(msg)
raise quota.QuotaError(msg, "MetadataLimitExceeded") raise quota.QuotaError(msg, "MetadataLimitExceeded")
def create(self, context, instance_type, def _check_create_parameters(self, context, instance_type,
image_href, kernel_id=None, ramdisk_id=None, image_href, kernel_id=None, ramdisk_id=None,
min_count=1, max_count=1, min_count=1, max_count=1,
display_name='', display_description='', display_name='', display_description='',
key_name=None, key_data=None, security_group='default', key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata={}, availability_zone=None, user_data=None, metadata={},
injected_files=None, injected_files=None, admin_password=None, zone_blob=None):
admin_password=None): """Verify all the input parameters regardless of the provisioning
"""Create the number and type of instances requested. strategy being performed."""
Verifies that quota and other arguments are valid.
"""
if not instance_type: if not instance_type:
instance_type = instance_types.get_default_instance_type() instance_type = instance_types.get_default_instance_type()
@ -225,63 +223,145 @@ class API(base.Base):
'metadata': metadata, 'metadata': metadata,
'availability_zone': availability_zone, 'availability_zone': availability_zone,
'os_type': os_type} 'os_type': os_type}
return (num_instances, base_options, security_groups)
def create_db_entry_for_new_instance(self, context, base_options,
security_groups, num=1):
"""Create an entry in the DB for this new instance,
including any related table updates (such as security
groups, MAC address, etc). This will called by create()
in the majority of situations, but all-at-once style
Schedulers may initiate the call."""
instance = dict(mac_address=utils.generate_mac(),
launch_index=num,
**base_options)
instance = self.db.instance_create(context, instance)
instance_id = instance['id']
elevated = context.elevated() elevated = context.elevated()
instances = [] if not security_groups:
LOG.debug(_("Going to run %s instances..."), num_instances) security_groups = []
for num in range(num_instances): for security_group_id in security_groups:
instance = dict(mac_address=utils.generate_mac(), self.db.instance_add_security_group(elevated,
launch_index=num, instance_id,
**base_options) security_group_id)
instance = self.db.instance_create(context, instance)
instance_id = instance['id']
elevated = context.elevated() # Set sane defaults if not specified
if not security_groups: updates = dict(hostname=self.hostname_factory(instance_id))
security_groups = [] if (not hasattr(instance, 'display_name') or
for security_group_id in security_groups: instance.display_name is None):
self.db.instance_add_security_group(elevated, updates['display_name'] = "Server %s" % instance_id
instance_id,
security_group_id)
# Set sane defaults if not specified instance = self.update(context, instance_id, **updates)
updates = dict(hostname=self.hostname_factory(instance_id))
if (not hasattr(instance, 'display_name') or
instance.display_name is None):
updates['display_name'] = "Server %s" % instance_id
instance = self.update(context, instance_id, **updates)
instances.append(instance)
pid = context.project_id
uid = context.user_id
LOG.debug(_("Casting to scheduler for %(pid)s/%(uid)s's"
" instance %(instance_id)s") % locals())
# NOTE(sandy): For now we're just going to pass in the
# instance_type record to the scheduler. In a later phase
# we'll be ripping this whole for-loop out and deferring the
# creation of the Instance record. At that point all this will
# change.
rpc.cast(context,
FLAGS.scheduler_topic,
{"method": "run_instance",
"args": {"topic": FLAGS.compute_topic,
"instance_id": instance_id,
"request_spec": {
'instance_type': instance_type,
'filter':
'nova.scheduler.host_filter.'
'InstanceTypeFilter',
},
"availability_zone": availability_zone,
"injected_files": injected_files,
"admin_password": admin_password,
},
})
for group_id in security_groups: for group_id in security_groups:
self.trigger_security_group_members_refresh(elevated, group_id) self.trigger_security_group_members_refresh(elevated, group_id)
return instance
def _ask_scheduler_to_create_instance(self, context, base_options,
instance_type, zone_blob,
availability_zone, injected_files,
admin_password,
instance_id=None, num_instances=1):
"""Send the run_instance request to the schedulers for processing."""
pid = context.project_id
uid = context.user_id
if instance_id:
LOG.debug(_("Casting to scheduler for %(pid)s/%(uid)s's"
" instance %(instance_id)s (single-shot)") % locals())
else:
LOG.debug(_("Casting to scheduler for %(pid)s/%(uid)s's"
" (all-at-once)") % locals())
filter_class = 'nova.scheduler.host_filter.InstanceTypeFilter'
request_spec = {
'instance_properties': base_options,
'instance_type': instance_type,
'filter': filter_class,
'blob': zone_blob,
'num_instances': num_instances
}
rpc.cast(context,
FLAGS.scheduler_topic,
{"method": "run_instance",
"args": {"topic": FLAGS.compute_topic,
"instance_id": instance_id,
"request_spec": request_spec,
"availability_zone": availability_zone,
"admin_password": admin_password,
"injected_files": injected_files}})
def create_all_at_once(self, context, instance_type,
image_href, kernel_id=None, ramdisk_id=None,
min_count=1, max_count=1,
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata={},
injected_files=None, admin_password=None, zone_blob=None):
"""Provision the instances by passing the whole request to
the Scheduler for execution. Returns a Reservation ID
related to the creation of all of these instances."""
num_instances, base_options, security_groups = \
self._check_create_parameters(
context, instance_type,
image_href, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
key_name, key_data, security_group,
availability_zone, user_data, metadata,
injected_files, admin_password, zone_blob)
self._ask_scheduler_to_create_instance(context, base_options,
instance_type, zone_blob,
availability_zone, injected_files,
admin_password,
num_instances=num_instances)
return base_options['reservation_id']
def create(self, context, instance_type,
image_href, kernel_id=None, ramdisk_id=None,
min_count=1, max_count=1,
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata={},
injected_files=None, admin_password=None, zone_blob=None):
"""
Provision the instances by sending off a series of single
instance requests to the Schedulers. This is fine for trival
Scheduler drivers, but may remove the effectiveness of the
more complicated drivers.
Returns a list of instance dicts.
"""
num_instances, base_options, security_groups = \
self._check_create_parameters(
context, instance_type,
image_href, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
key_name, key_data, security_group,
availability_zone, user_data, metadata,
injected_files, admin_password, zone_blob)
instances = []
LOG.debug(_("Going to run %s instances..."), num_instances)
for num in range(num_instances):
instance = self.create_db_entry_for_new_instance(context,
base_options, security_groups, num=num)
instances.append(instance)
instance_id = instance['id']
self._ask_scheduler_to_create_instance(context, base_options,
instance_type, zone_blob,
availability_zone, injected_files,
admin_password,
instance_id=instance_id)
return [dict(x.iteritems()) for x in instances] return [dict(x.iteritems()) for x in instances]
def has_finished_migration(self, context, instance_id): def has_finished_migration(self, context, instance_id):

View File

@ -114,7 +114,7 @@ def get_instance_type(id):
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
return db.instance_type_get_by_id(ctxt, id) return db.instance_type_get_by_id(ctxt, id)
except exception.DBError: except exception.DBError:
raise exception.ApiError(_("Unknown instance type: %s") % name) raise exception.ApiError(_("Unknown instance type: %s") % id)
def get_instance_type_by_name(name): def get_instance_type_by_name(name):

View File

@ -36,6 +36,7 @@ from twisted.application import service
from nova import flags from nova import flags
from nova import log as logging from nova import log as logging
from nova import utils
from nova.virt import connection as virt_connection from nova.virt import connection as virt_connection

View File

@ -119,7 +119,7 @@ class VMRCSessionConsole(VMRCConsole):
""" """
vms = vim_session._call_method(vim_util, 'get_objects', vms = vim_session._call_method(vim_util, 'get_objects',
'VirtualMachine', ['name']) 'VirtualMachine', ['name'])
vm_ref = NoneV vm_ref = None
for vm in vms: for vm in vms:
if vm.propSet[0].val == instance_name: if vm.propSet[0].val == instance_name:
vm_ref = vm.obj vm_ref = vm.obj

View File

@ -743,7 +743,7 @@ def fixed_ip_get_all_by_instance(context, instance_id):
filter_by(instance_id=instance_id).\ filter_by(instance_id=instance_id).\
filter_by(deleted=False) filter_by(deleted=False)
if not rv: if not rv:
raise exception.NoFloatingIpsFoundForInstance(instance_id=instance_id) raise exception.NoFixedIpsFoundForInstance(instance_id=instance_id)
return rv return rv

View File

@ -0,0 +1,65 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2011 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 MetaData, Table
meta = MetaData()
def upgrade(migrate_engine):
# Upgrade operations go here. Don't create your own engine;
# bind migrate_engine to your metadata
meta.bind = migrate_engine
if migrate_engine.name == "mysql":
migrate_engine.execute("ALTER TABLE auth_tokens Engine=InnoDB")
migrate_engine.execute("ALTER TABLE certificates Engine=InnoDB")
migrate_engine.execute("ALTER TABLE compute_nodes Engine=InnoDB")
migrate_engine.execute("ALTER TABLE console_pools Engine=InnoDB")
migrate_engine.execute("ALTER TABLE consoles Engine=InnoDB")
migrate_engine.execute("ALTER TABLE export_devices Engine=InnoDB")
migrate_engine.execute("ALTER TABLE fixed_ips Engine=InnoDB")
migrate_engine.execute("ALTER TABLE floating_ips Engine=InnoDB")
migrate_engine.execute("ALTER TABLE instance_actions Engine=InnoDB")
migrate_engine.execute("ALTER TABLE instance_metadata Engine=InnoDB")
migrate_engine.execute("ALTER TABLE instance_types Engine=InnoDB")
migrate_engine.execute("ALTER TABLE instances Engine=InnoDB")
migrate_engine.execute("ALTER TABLE iscsi_targets Engine=InnoDB")
migrate_engine.execute("ALTER TABLE key_pairs Engine=InnoDB")
migrate_engine.execute("ALTER TABLE migrate_version Engine=InnoDB")
migrate_engine.execute("ALTER TABLE migrations Engine=InnoDB")
migrate_engine.execute("ALTER TABLE networks Engine=InnoDB")
migrate_engine.execute("ALTER TABLE projects Engine=InnoDB")
migrate_engine.execute("ALTER TABLE quotas Engine=InnoDB")
migrate_engine.execute(
"ALTER TABLE security_group_instance_association Engine=InnoDB")
migrate_engine.execute(
"ALTER TABLE security_group_rules Engine=InnoDB")
migrate_engine.execute("ALTER TABLE security_groups Engine=InnoDB")
migrate_engine.execute("ALTER TABLE services Engine=InnoDB")
migrate_engine.execute(
"ALTER TABLE user_project_association Engine=InnoDB")
migrate_engine.execute(
"ALTER TABLE user_project_role_association Engine=InnoDB")
migrate_engine.execute(
"ALTER TABLE user_role_association Engine=InnoDB")
migrate_engine.execute("ALTER TABLE users Engine=InnoDB")
migrate_engine.execute("ALTER TABLE volumes Engine=InnoDB")
migrate_engine.execute("ALTER TABLE zones Engine=InnoDB")
migrate_engine.execute("ALTER TABLE snapshots Engine=InnoDB")
def downgrade(migrate_engine):
meta.bind = migrate_engine

View File

@ -46,6 +46,7 @@ class NovaBase(object):
updated_at = Column(DateTime, onupdate=utils.utcnow) updated_at = Column(DateTime, onupdate=utils.utcnow)
deleted_at = Column(DateTime) deleted_at = Column(DateTime)
deleted = Column(Boolean, default=False) deleted = Column(Boolean, default=False)
metadata = None
def save(self, session=None): def save(self, session=None):
"""Save this object.""" """Save this object."""

View File

@ -376,6 +376,10 @@ class NoFloatingIpsDefinedForInstance(NoFloatingIpsDefined):
message = _("Zero floating ips defined for instance %(instance_id)s.") message = _("Zero floating ips defined for instance %(instance_id)s.")
class NoMoreFloatingIps(NotFound):
message = _("Zero floating ips available.")
class KeypairNotFound(NotFound): class KeypairNotFound(NotFound):
message = _("Keypair %(keypair_name)s not found for user %(user_id)s") message = _("Keypair %(keypair_name)s not found for user %(user_id)s")

View File

@ -270,8 +270,10 @@ DEFINE_list('region_list',
DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake') DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID') DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key') DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
DEFINE_integer('glance_port', 9292, 'glance port') # NOTE(sirp): my_ip interpolation doesn't work within nested structures
DEFINE_string('glance_host', '$my_ip', 'glance host') DEFINE_list('glance_api_servers',
['127.0.0.1:9292'],
'list of glance api servers available to nova (host:port)')
DEFINE_integer('s3_port', 3333, 's3 port') DEFINE_integer('s3_port', 3333, 's3 port')
DEFINE_string('s3_host', '$my_ip', 's3 host (for infrastructure)') DEFINE_string('s3_host', '$my_ip', 's3 host (for infrastructure)')
DEFINE_string('s3_dmz', '$my_ip', 's3 dmz ip (for instances)') DEFINE_string('s3_dmz', '$my_ip', 's3 dmz ip (for instances)')
@ -381,3 +383,5 @@ DEFINE_string('zone_name', 'nova', 'name of this zone')
DEFINE_list('zone_capabilities', DEFINE_list('zone_capabilities',
['hypervisor=xenserver;kvm', 'os=linux;windows'], ['hypervisor=xenserver;kvm', 'os=linux;windows'],
'Key/Multi-value list representng capabilities of this zone') 'Key/Multi-value list representng capabilities of this zone')
DEFINE_string('build_plan_encryption_key', None,
'128bit (hex) encryption key for scheduler build plans.')

View File

@ -22,6 +22,7 @@ import nova
from nova import exception from nova import exception
from nova import utils from nova import utils
from nova import flags from nova import flags
from nova.image import glance as glance_image_service
FLAGS = flags.FLAGS FLAGS = flags.FLAGS
@ -48,6 +49,8 @@ def get_default_image_service():
return ImageService() return ImageService()
# FIXME(sirp): perhaps this should be moved to nova/images/glance so that we
# keep Glance specific code together for the most part
def get_glance_client(image_href): def get_glance_client(image_href):
"""Get the correct glance client and id for the given image_href. """Get the correct glance client and id for the given image_href.
@ -62,7 +65,9 @@ def get_glance_client(image_href):
""" """
image_href = image_href or 0 image_href = image_href or 0
if str(image_href).isdigit(): if str(image_href).isdigit():
glance_client = GlanceClient(FLAGS.glance_host, FLAGS.glance_port) glance_host, glance_port = \
glance_image_service.pick_glance_api_server()
glance_client = GlanceClient(glance_host, glance_port)
return (glance_client, int(image_href)) return (glance_client, int(image_href))
try: try:

View File

@ -20,6 +20,7 @@
from __future__ import absolute_import from __future__ import absolute_import
import datetime import datetime
import random
from glance.common import exception as glance_exception from glance.common import exception as glance_exception
@ -39,6 +40,21 @@ FLAGS = flags.FLAGS
GlanceClient = utils.import_class('glance.client.Client') GlanceClient = utils.import_class('glance.client.Client')
def pick_glance_api_server():
"""Return which Glance API server to use for the request
This method provides a very primitive form of load-balancing suitable for
testing and sandbox environments. In production, it would be better to use
one IP and route that to a real load-balancer.
Returns (host, port)
"""
host_port = random.choice(FLAGS.glance_api_servers)
host, port_str = host_port.split(':')
port = int(port_str)
return host, port
class GlanceImageService(service.BaseImageService): class GlanceImageService(service.BaseImageService):
"""Provides storage and retrieval of disk image objects within Glance.""" """Provides storage and retrieval of disk image objects within Glance."""
@ -51,12 +67,21 @@ class GlanceImageService(service.BaseImageService):
GLANCE_ONLY_ATTRS GLANCE_ONLY_ATTRS
def __init__(self, client=None): def __init__(self, client=None):
# FIXME(sirp): can we avoid dependency-injection here by using self._client = client
# stubbing out a fake?
if client is None: def _get_client(self):
self.client = GlanceClient(FLAGS.glance_host, FLAGS.glance_port) # NOTE(sirp): we want to load balance each request across glance
else: # servers. Since GlanceImageService is a long-lived object, `client`
self.client = client # is made to choose a new server each time via this property.
if self._client is not None:
return self._client
glance_host, glance_port = pick_glance_api_server()
return GlanceClient(glance_host, glance_port)
def _set_client(self, client):
self._client = client
client = property(_get_client, _set_client)
def index(self, context, filters=None, marker=None, limit=None): def index(self, context, filters=None, marker=None, limit=None):
"""Calls out to Glance for a list of images available.""" """Calls out to Glance for a list of images available."""

View File

@ -86,6 +86,7 @@ flags.DEFINE_string('floating_range', '4.4.4.0/24',
'Floating IP address block') 'Floating IP address block')
flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block') flags.DEFINE_string('fixed_range', '10.0.0.0/8', 'Fixed IP address block')
flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block') flags.DEFINE_string('fixed_range_v6', 'fd00::/48', 'Fixed IPv6 address block')
flags.DEFINE_string('gateway_v6', None, 'Default IPv6 gateway')
flags.DEFINE_integer('cnt_vpn_clients', 0, flags.DEFINE_integer('cnt_vpn_clients', 0,
'Number of addresses reserved for vpn clients') 'Number of addresses reserved for vpn clients')
flags.DEFINE_string('network_driver', 'nova.network.linux_net', flags.DEFINE_string('network_driver', 'nova.network.linux_net',
@ -292,7 +293,7 @@ class NetworkManager(manager.SchedulerDependentManager):
return host return host
def create_networks(self, context, cidr, num_networks, network_size, def create_networks(self, context, cidr, num_networks, network_size,
cidr_v6, label, *args, **kwargs): cidr_v6, gateway_v6, label, *args, **kwargs):
"""Create networks based on parameters.""" """Create networks based on parameters."""
fixed_net = IPy.IP(cidr) fixed_net = IPy.IP(cidr)
fixed_net_v6 = IPy.IP(cidr_v6) fixed_net_v6 = IPy.IP(cidr_v6)
@ -324,7 +325,13 @@ class NetworkManager(manager.SchedulerDependentManager):
significant_bits_v6) significant_bits_v6)
net['cidr_v6'] = cidr_v6 net['cidr_v6'] = cidr_v6
project_net_v6 = IPy.IP(cidr_v6) project_net_v6 = IPy.IP(cidr_v6)
net['gateway_v6'] = str(project_net_v6[1])
if gateway_v6:
# use a pre-defined gateway if one is provided
net['gateway_v6'] = str(gateway_v6)
else:
net['gateway_v6'] = str(project_net_v6[1])
net['netmask_v6'] = str(project_net_v6.prefixlen()) net['netmask_v6'] = str(project_net_v6.prefixlen())
network_ref = self.db.network_create_safe(context, net) network_ref = self.db.network_create_safe(context, net)

View File

@ -84,7 +84,7 @@ def get_zone_capabilities(context):
def select(context, specs=None): def select(context, specs=None):
"""Returns a list of hosts.""" """Returns a list of hosts."""
return _call_scheduler('select', context=context, return _call_scheduler('select', context=context,
params={"specs": specs}) params={"request_spec": specs})
def update_service_capabilities(context, service_name, host, capabilities): def update_service_capabilities(context, service_name, host, capabilities):

View File

@ -70,6 +70,14 @@ class SchedulerManager(manager.Manager):
self.zone_manager.update_service_capabilities(service_name, self.zone_manager.update_service_capabilities(service_name,
host, capabilities) host, capabilities)
def select(self, context=None, *args, **kwargs):
"""Select a list of hosts best matching the provided specs."""
return self.driver.select(context, *args, **kwargs)
def get_scheduler_rules(self, context=None, *args, **kwargs):
"""Ask the driver how requests should be made of it."""
return self.driver.get_scheduler_rules(context, *args, **kwargs)
def _schedule(self, method, context, topic, *args, **kwargs): def _schedule(self, method, context, topic, *args, **kwargs):
"""Tries to call schedule_* method on the driver to retrieve host. """Tries to call schedule_* method on the driver to retrieve host.
@ -80,7 +88,9 @@ class SchedulerManager(manager.Manager):
try: try:
host = getattr(self.driver, driver_method)(elevated, *args, host = getattr(self.driver, driver_method)(elevated, *args,
**kwargs) **kwargs)
except AttributeError: except AttributeError, e:
LOG.exception(_("Driver Method %(driver_method)s missing: %(e)s")
% locals())
host = self.driver.schedule(elevated, topic, *args, **kwargs) host = self.driver.schedule(elevated, topic, *args, **kwargs)
if not host: if not host:

View File

@ -21,16 +21,30 @@ across zones. There are two expansion points to this class for:
""" """
import operator import operator
import json
import M2Crypto
import novaclient
from nova import crypto
from nova import db from nova import db
from nova import exception
from nova import flags
from nova import log as logging from nova import log as logging
from nova import rpc from nova import rpc
from nova.scheduler import api from nova.scheduler import api
from nova.scheduler import driver from nova.scheduler import driver
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.scheduler.zone_aware_scheduler') LOG = logging.getLogger('nova.scheduler.zone_aware_scheduler')
class InvalidBlob(exception.NovaException):
message = _("Ill-formed or incorrectly routed 'blob' data sent "
"to instance create request.")
class ZoneAwareScheduler(driver.Scheduler): class ZoneAwareScheduler(driver.Scheduler):
"""Base class for creating Zone Aware Schedulers.""" """Base class for creating Zone Aware Schedulers."""
@ -38,6 +52,112 @@ class ZoneAwareScheduler(driver.Scheduler):
"""Call novaclient zone method. Broken out for testing.""" """Call novaclient zone method. Broken out for testing."""
return api.call_zone_method(context, method, specs=specs) return api.call_zone_method(context, method, specs=specs)
def _provision_resource_locally(self, context, item, instance_id, kwargs):
"""Create the requested resource in this Zone."""
host = item['hostname']
kwargs['instance_id'] = instance_id
rpc.cast(context,
db.queue_get_for(context, "compute", host),
{"method": "run_instance",
"args": kwargs})
LOG.debug(_("Provisioning locally via compute node %(host)s")
% locals())
def _decrypt_blob(self, blob):
"""Returns the decrypted blob or None if invalid. Broken out
for testing."""
decryptor = crypto.decryptor(FLAGS.build_plan_encryption_key)
try:
json_entry = decryptor(blob)
return json.dumps(entry)
except M2Crypto.EVP.EVPError:
pass
return None
def _ask_child_zone_to_create_instance(self, context, zone_info,
request_spec, kwargs):
"""Once we have determined that the request should go to one
of our children, we need to fabricate a new POST /servers/
call with the same parameters that were passed into us.
Note that we have to reverse engineer from our args to get back the
image, flavor, ipgroup, etc. since the original call could have
come in from EC2 (which doesn't use these things)."""
instance_type = request_spec['instance_type']
instance_properties = request_spec['instance_properties']
name = instance_properties['display_name']
image_id = instance_properties['image_id']
meta = instance_properties['metadata']
flavor_id = instance_type['flavorid']
files = kwargs['injected_files']
ipgroup = None # Not supported in OS API ... yet
child_zone = zone_info['child_zone']
child_blob = zone_info['child_blob']
zone = db.zone_get(context, child_zone)
url = zone.api_url
LOG.debug(_("Forwarding instance create call to child zone %(url)s")
% locals())
nova = None
try:
nova = novaclient.OpenStack(zone.username, zone.password, url)
nova.authenticate()
except novaclient.exceptions.BadRequest, e:
raise exception.NotAuthorized(_("Bad credentials attempting "
"to talk to zone at %(url)s.") % locals())
nova.servers.create(name, image_id, flavor_id, ipgroup, meta, files,
child_blob)
def _provision_resource_from_blob(self, context, item, instance_id,
request_spec, kwargs):
"""Create the requested resource locally or in a child zone
based on what is stored in the zone blob info.
Attempt to decrypt the blob to see if this request is:
1. valid, and
2. intended for this zone or a child zone.
Note: If we have "blob" that means the request was passed
into us from a parent zone. If we have "child_blob" that
means we gathered the info from one of our children.
It's possible that, when we decrypt the 'blob' field, it
contains "child_blob" data. In which case we forward the
request."""
host_info = None
if "blob" in item:
# Request was passed in from above. Is it for us?
host_info = self._decrypt_blob(item['blob'])
elif "child_blob" in item:
# Our immediate child zone provided this info ...
host_info = item
if not host_info:
raise InvalidBlob()
# Valid data ... is it for us?
if 'child_zone' in host_info and 'child_blob' in host_info:
self._ask_child_zone_to_create_instance(context, host_info,
request_spec, kwargs)
else:
self._provision_resource_locally(context, host_info,
instance_id, kwargs)
def _provision_resource(self, context, item, instance_id, request_spec,
kwargs):
"""Create the requested resource in this Zone or a child zone."""
if "hostname" in item:
self._provision_resource_locally(context, item, instance_id,
kwargs)
return
self._provision_resource_from_blob(context, item, instance_id,
request_spec, kwargs)
def schedule_run_instance(self, context, instance_id, request_spec, def schedule_run_instance(self, context, instance_id, request_spec,
*args, **kwargs): *args, **kwargs):
"""This method is called from nova.compute.api to provision """This method is called from nova.compute.api to provision
@ -51,8 +171,10 @@ class ZoneAwareScheduler(driver.Scheduler):
# TODO(sandy): We'll have to look for richer specs at some point. # TODO(sandy): We'll have to look for richer specs at some point.
if 'blob' in request_spec: blob = request_spec.get('blob')
self.provision_resource(context, request_spec, instance_id, kwargs) if blob:
self._provision_resource(context, request_spec, instance_id,
request_spec, kwargs)
return None return None
# Create build plan and provision ... # Create build plan and provision ...
@ -61,28 +183,13 @@ class ZoneAwareScheduler(driver.Scheduler):
raise driver.NoValidHost(_('No hosts were available')) raise driver.NoValidHost(_('No hosts were available'))
for item in build_plan: for item in build_plan:
self.provision_resource(context, item, instance_id, kwargs) self._provision_resource(context, item, instance_id, request_spec,
kwargs)
# Returning None short-circuits the routing to Compute (since # Returning None short-circuits the routing to Compute (since
# we've already done it here) # we've already done it here)
return None return None
def provision_resource(self, context, item, instance_id, kwargs):
"""Create the requested resource in this Zone or a child zone."""
if "hostname" in item:
host = item['hostname']
kwargs['instance_id'] = instance_id
rpc.cast(context,
db.queue_get_for(context, "compute", host),
{"method": "run_instance",
"args": kwargs})
LOG.debug(_("Casted to compute %(host)s for run_instance")
% locals())
else:
# TODO(sandy) Provision in child zone ...
LOG.warning(_("Provision to Child Zone not supported (yet)"))
pass
def select(self, context, request_spec, *args, **kwargs): def select(self, context, request_spec, *args, **kwargs):
"""Select returns a list of weights and zone/host information """Select returns a list of weights and zone/host information
corresponding to the best hosts to service the request. Any corresponding to the best hosts to service the request. Any
@ -124,17 +231,17 @@ class ZoneAwareScheduler(driver.Scheduler):
weighted = self.weigh_hosts(num_instances, request_spec, host_list) weighted = self.weigh_hosts(num_instances, request_spec, host_list)
# Next, tack on the best weights from the child zones ... # Next, tack on the best weights from the child zones ...
json_spec = json.dumps(request_spec)
child_results = self._call_zone_method(context, "select", child_results = self._call_zone_method(context, "select",
specs=request_spec) specs=json_spec)
for child_zone, result in child_results: for child_zone, result in child_results:
for weighting in result: for weighting in result:
# Remember the child_zone so we can get back to # Remember the child_zone so we can get back to
# it later if needed. This implicitly builds a zone # it later if needed. This implicitly builds a zone
# path structure. # path structure.
host_dict = { host_dict = {"weight": weighting["weight"],
"weight": weighting["weight"], "child_zone": child_zone,
"child_zone": child_zone, "child_blob": weighting["blob"]}
"child_blob": weighting["blob"]}
weighted.append(host_dict) weighted.append(host_dict)
weighted.sort(key=operator.itemgetter('weight')) weighted.sort(key=operator.itemgetter('weight'))

View File

@ -352,6 +352,11 @@ class FakeAuthManager(object):
return user.admin return user.admin
def is_project_member(self, user, project): def is_project_member(self, user, project):
if not isinstance(project, Project):
try:
project = self.get_project(project)
except exc.NotFound:
raise webob.exc.HTTPUnauthorized()
return ((user.id in project.member_ids) or return ((user.id in project.member_ids) or
(user.id == project.project_manager_id)) (user.id == project.project_manager_id))

View File

@ -114,6 +114,28 @@ class Test(test.TestCase):
self.assertEqual(result.status, '401 Unauthorized') self.assertEqual(result.status, '401 Unauthorized')
self.assertEqual(self.destroy_called, True) self.assertEqual(self.destroy_called, True)
def test_authorize_project(self):
f = fakes.FakeAuthManager()
user = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
f.add_user(user)
f.create_project('user1_project', user)
f.create_project('user2_project', user)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
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')
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouterV10', fakes.FakeRouter)
req = webob.Request.blank('/v1.0/fake')
req.headers['X-Auth-Token'] = token
req.headers['X-Auth-Project-Id'] = 'user2_project'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '200 OK')
self.assertEqual(result.headers['X-Test-Success'], 'True')
def test_bad_user_bad_key(self): def test_bad_user_bad_key(self):
req = webob.Request.blank('/v1.0/') req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'unknown_user' req.headers['X-Auth-User'] = 'unknown_user'
@ -143,6 +165,49 @@ class Test(test.TestCase):
result = req.get_response(fakes.wsgi_app()) result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '401 Unauthorized') self.assertEqual(result.status, '401 Unauthorized')
def test_bad_project(self):
f = fakes.FakeAuthManager()
user1 = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
user2 = nova.auth.manager.User('id2', 'user2', 'user2_key', None, None)
f.add_user(user1)
f.add_user(user2)
f.create_project('user1_project', user1)
f.create_project('user2_project', user2)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
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')
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouterV10', fakes.FakeRouter)
req = webob.Request.blank('/v1.0/fake')
req.headers['X-Auth-Token'] = token
req.headers['X-Auth-Project-Id'] = 'user2_project'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '401 Unauthorized')
def test_not_existing_project(self):
f = fakes.FakeAuthManager()
user1 = nova.auth.manager.User('id1', 'user1', 'user1_key', None, None)
f.add_user(user1)
f.create_project('user1_project', user1)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
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')
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouterV10', fakes.FakeRouter)
req = webob.Request.blank('/v1.0/fake')
req.headers['X-Auth-Token'] = token
req.headers['X-Auth-Project-Id'] = 'unknown_project'
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '401 Unauthorized')
class TestFunctional(test.TestCase): class TestFunctional(test.TestCase):
def test_token_expiry(self): def test_token_expiry(self):

View File

@ -21,7 +21,6 @@ import json
import nova.db import nova.db
from nova import context from nova import context
from nova import crypto from nova import crypto
from nova import exception
from nova import flags from nova import flags
from nova import test from nova import test
from nova.api.openstack import zones from nova.api.openstack import zones
@ -210,6 +209,11 @@ class ZonesTest(test.TestCase):
self.stubs.Set(api, 'select', zone_select) self.stubs.Set(api, 'select', zone_select)
req = webob.Request.blank('/v1.0/zones/select') req = webob.Request.blank('/v1.0/zones/select')
req.method = 'POST'
req.headers["Content-Type"] = "application/json"
# Select queries end up being JSON encoded twice.
# Once to a string and again as an HTTP POST Body
req.body = json.dumps(json.dumps({}))
res = req.get_response(fakes.wsgi_app()) res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body) res_dict = json.loads(res.body)

View File

@ -60,10 +60,8 @@ class BaseGlanceTest(unittest.TestCase):
NOW_DATETIME = datetime.datetime(2010, 10, 11, 10, 30, 22) NOW_DATETIME = datetime.datetime(2010, 10, 11, 10, 30, 22)
def setUp(self): def setUp(self):
# FIXME(sirp): we can probably use stubs library here rather than
# dependency injection
self.client = StubGlanceClient(None) self.client = StubGlanceClient(None)
self.service = glance.GlanceImageService(self.client) self.service = glance.GlanceImageService(client=self.client)
self.context = context.RequestContext(None, None) self.context = context.RequestContext(None, None)
def assertDateTimesFilled(self, image_meta): def assertDateTimesFilled(self, image_meta):

View File

@ -16,6 +16,7 @@
Tests For Zone Aware Scheduler. Tests For Zone Aware Scheduler.
""" """
from nova import exception
from nova import test from nova import test
from nova.scheduler import driver from nova.scheduler import driver
from nova.scheduler import zone_aware_scheduler from nova.scheduler import zone_aware_scheduler
@ -90,6 +91,41 @@ def fake_empty_call_zone_method(context, method, specs):
return [] return []
# Hmm, I should probably be using mox for this.
was_called = False
def fake_provision_resource(context, item, instance_id, request_spec, kwargs):
global was_called
was_called = True
def fake_ask_child_zone_to_create_instance(context, zone_info,
request_spec, kwargs):
global was_called
was_called = True
def fake_provision_resource_locally(context, item, instance_id, kwargs):
global was_called
was_called = True
def fake_provision_resource_from_blob(context, item, instance_id,
request_spec, kwargs):
global was_called
was_called = True
def fake_decrypt_blob_returns_local_info(blob):
return {'foo': True} # values aren't important.
def fake_decrypt_blob_returns_child_info(blob):
return {'child_zone': True,
'child_blob': True} # values aren't important. Keys are.
def fake_call_zone_method(context, method, specs): def fake_call_zone_method(context, method, specs):
return [ return [
('zone1', [ ('zone1', [
@ -149,4 +185,112 @@ class ZoneAwareSchedulerTestCase(test.TestCase):
fake_context = {} fake_context = {}
self.assertRaises(driver.NoValidHost, sched.schedule_run_instance, self.assertRaises(driver.NoValidHost, sched.schedule_run_instance,
fake_context, 1, fake_context, 1,
dict(host_filter=None, instance_type={})) dict(host_filter=None,
request_spec={'instance_type': {}}))
def test_schedule_do_not_schedule_with_hint(self):
"""
Check the local/child zone routing in the run_instance() call.
If the zone_blob hint was passed in, don't re-schedule.
"""
global was_called
sched = FakeZoneAwareScheduler()
was_called = False
self.stubs.Set(sched, '_provision_resource', fake_provision_resource)
request_spec = {
'instance_properties': {},
'instance_type': {},
'filter_driver': 'nova.scheduler.host_filter.AllHostsFilter',
'blob': "Non-None blob data"
}
result = sched.schedule_run_instance(None, 1, request_spec)
self.assertEquals(None, result)
self.assertTrue(was_called)
def test_provision_resource_local(self):
"""Provision a resource locally or remotely."""
global was_called
sched = FakeZoneAwareScheduler()
was_called = False
self.stubs.Set(sched, '_provision_resource_locally',
fake_provision_resource_locally)
request_spec = {'hostname': "foo"}
sched._provision_resource(None, request_spec, 1, request_spec, {})
self.assertTrue(was_called)
def test_provision_resource_remote(self):
"""Provision a resource locally or remotely."""
global was_called
sched = FakeZoneAwareScheduler()
was_called = False
self.stubs.Set(sched, '_provision_resource_from_blob',
fake_provision_resource_from_blob)
request_spec = {}
sched._provision_resource(None, request_spec, 1, request_spec, {})
self.assertTrue(was_called)
def test_provision_resource_from_blob_empty(self):
"""Provision a resource locally or remotely given no hints."""
global was_called
sched = FakeZoneAwareScheduler()
request_spec = {}
self.assertRaises(zone_aware_scheduler.InvalidBlob,
sched._provision_resource_from_blob,
None, {}, 1, {}, {})
def test_provision_resource_from_blob_with_local_blob(self):
"""
Provision a resource locally or remotely when blob hint passed in.
"""
global was_called
sched = FakeZoneAwareScheduler()
was_called = False
self.stubs.Set(sched, '_decrypt_blob',
fake_decrypt_blob_returns_local_info)
self.stubs.Set(sched, '_provision_resource_locally',
fake_provision_resource_locally)
request_spec = {'blob': "Non-None blob data"}
sched._provision_resource_from_blob(None, request_spec, 1,
request_spec, {})
self.assertTrue(was_called)
def test_provision_resource_from_blob_with_child_blob(self):
"""
Provision a resource locally or remotely when child blob hint
passed in.
"""
global was_called
sched = FakeZoneAwareScheduler()
self.stubs.Set(sched, '_decrypt_blob',
fake_decrypt_blob_returns_child_info)
was_called = False
self.stubs.Set(sched, '_ask_child_zone_to_create_instance',
fake_ask_child_zone_to_create_instance)
request_spec = {'blob': "Non-None blob data"}
sched._provision_resource_from_blob(None, request_spec, 1,
request_spec, {})
self.assertTrue(was_called)
def test_provision_resource_from_blob_with_immediate_child_blob(self):
"""
Provision a resource locally or remotely when blob hint passed in
from an immediate child.
"""
global was_called
sched = FakeZoneAwareScheduler()
was_called = False
self.stubs.Set(sched, '_ask_child_zone_to_create_instance',
fake_ask_child_zone_to_create_instance)
request_spec = {'child_blob': True, 'child_zone': True}
sched._provision_resource_from_blob(None, request_spec, 1,
request_spec, {})
self.assertTrue(was_called)

View File

@ -115,6 +115,18 @@ class CloudTestCase(test.TestCase):
public_ip=address) public_ip=address)
db.floating_ip_destroy(self.context, address) db.floating_ip_destroy(self.context, address)
def test_allocate_address(self):
address = "10.10.10.10"
allocate = self.cloud.allocate_address
db.floating_ip_create(self.context,
{'address': address,
'host': self.network.host})
self.assertEqual(allocate(self.context)['publicIp'], address)
db.floating_ip_destroy(self.context, address)
self.assertRaises(exception.NoMoreFloatingIps,
allocate,
self.context)
def test_associate_disassociate_address(self): def test_associate_disassociate_address(self):
"""Verifies associate runs cleanly without raising an exception""" """Verifies associate runs cleanly without raising an exception"""
address = "10.10.10.10" address = "10.10.10.10"
@ -487,6 +499,21 @@ class CloudTestCase(test.TestCase):
self.assertRaises(exception.ApiError, run_instances, self.assertRaises(exception.ApiError, run_instances,
self.context, **kwargs) self.context, **kwargs)
def test_run_instances_image_status_active(self):
kwargs = {'image_id': FLAGS.default_image,
'instance_type': FLAGS.default_instance_type,
'max_count': 1}
run_instances = self.cloud.run_instances
def fake_show_stat_active(self, context, id):
return {'id': 1, 'properties': {'kernel_id': 1, 'ramdisk_id': 1,
'type': 'machine'}, 'status': 'active'}
self.stubs.Set(local.LocalImageService, 'show', fake_show_stat_active)
result = run_instances(self.context, **kwargs)
self.assertEqual(len(result['instancesSet']), 1)
def test_terminate_instances(self): def test_terminate_instances(self):
inst1 = db.instance_create(self.context, {'reservation_id': 'a', inst1 = db.instance_create(self.context, {'reservation_id': 'a',
'image_ref': 1, 'image_ref': 1,

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import copy
import eventlet import eventlet
import mox import mox
import os import os
@ -125,6 +126,7 @@ class CacheConcurrencyTestCase(test.TestCase):
class LibvirtConnTestCase(test.TestCase): class LibvirtConnTestCase(test.TestCase):
def setUp(self): def setUp(self):
super(LibvirtConnTestCase, self).setUp() super(LibvirtConnTestCase, self).setUp()
connection._late_load_cheetah() connection._late_load_cheetah()
@ -207,6 +209,29 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn') self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn')
connection.LibvirtConnection._conn = fake connection.LibvirtConnection._conn = fake
def fake_lookup(self, instance_name):
class FakeVirtDomain(object):
def snapshotCreateXML(self, *args):
return None
def XMLDesc(self, *args):
return """
<domain type='kvm'>
<devices>
<disk type='file'>
<source file='filename'/>
</disk>
</devices>
</domain>
"""
return FakeVirtDomain()
def fake_execute(self, *args):
open(args[-1], "a").close()
def create_service(self, **kwargs): def create_service(self, **kwargs):
service_ref = {'host': kwargs.get('host', 'dummy'), service_ref = {'host': kwargs.get('host', 'dummy'),
'binary': 'nova-compute', 'binary': 'nova-compute',
@ -283,38 +308,11 @@ class LibvirtConnTestCase(test.TestCase):
self._check_xml_and_container(instance_data) self._check_xml_and_container(instance_data)
def test_snapshot(self): def test_snapshot(self):
if not self.lazy_load_library_exists():
return
FLAGS.image_service = 'nova.image.fake.FakeImageService' FLAGS.image_service = 'nova.image.fake.FakeImageService'
# Only file-based instance storages are supported at the moment
test_xml = """
<domain type='kvm'>
<devices>
<disk type='file'>
<source file='filename'/>
</disk>
</devices>
</domain>
"""
class FakeVirtDomain(object):
def __init__(self):
pass
def snapshotCreateXML(self, *args):
return None
def XMLDesc(self, *args):
return test_xml
def fake_lookup(instance_name):
if instance_name == instance_ref.name:
return FakeVirtDomain()
def fake_execute(*args):
# Touch filename to pass 'with open(out_path)'
open(args[-1], "a").close()
# Start test # Start test
image_service = utils.import_object(FLAGS.image_service) image_service = utils.import_object(FLAGS.image_service)
@ -330,9 +328,49 @@ class LibvirtConnTestCase(test.TestCase):
recv_meta = image_service.create(context, sent_meta) recv_meta = image_service.create(context, sent_meta)
self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn') self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn')
connection.LibvirtConnection._conn.lookupByName = fake_lookup connection.LibvirtConnection._conn.lookupByName = self.fake_lookup
self.mox.StubOutWithMock(connection.utils, 'execute') self.mox.StubOutWithMock(connection.utils, 'execute')
connection.utils.execute = fake_execute connection.utils.execute = self.fake_execute
self.mox.ReplayAll()
conn = connection.LibvirtConnection(False)
conn.snapshot(instance_ref, recv_meta['id'])
snapshot = image_service.show(context, recv_meta['id'])
self.assertEquals(snapshot['properties']['image_state'], 'available')
self.assertEquals(snapshot['status'], 'active')
self.assertEquals(snapshot['name'], snapshot_name)
def test_snapshot_no_image_architecture(self):
if not self.lazy_load_library_exists():
return
FLAGS.image_service = 'nova.image.fake.FakeImageService'
# Start test
image_service = utils.import_object(FLAGS.image_service)
# Assign image_ref = 2 from nova/images/fakes for testing different
# base image
test_instance = copy.deepcopy(self.test_instance)
test_instance["image_ref"] = "2"
# Assuming that base image already exists in image_service
instance_ref = db.instance_create(self.context, test_instance)
properties = {'instance_id': instance_ref['id'],
'user_id': str(self.context.user_id)}
snapshot_name = 'test-snap'
sent_meta = {'name': snapshot_name, 'is_public': False,
'status': 'creating', 'properties': properties}
# Create new image. It will be updated in snapshot method
# To work with it from snapshot, the single image_service is needed
recv_meta = image_service.create(context, sent_meta)
self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn')
connection.LibvirtConnection._conn.lookupByName = self.fake_lookup
self.mox.StubOutWithMock(connection.utils, 'execute')
connection.utils.execute = self.fake_execute
self.mox.ReplayAll() self.mox.ReplayAll()

View File

@ -69,7 +69,7 @@ class VMWareAPIVMTestCase(test.TestCase):
'instance_type': 'm1.large', 'instance_type': 'm1.large',
'mac_address': 'aa:bb:cc:dd:ee:ff', 'mac_address': 'aa:bb:cc:dd:ee:ff',
} }
self.instance = db.instance_create(values) self.instance = db.instance_create(None, values)
def _create_vm(self): def _create_vm(self):
"""Create and spawn the VM.""" """Create and spawn the VM."""

View File

@ -52,7 +52,7 @@ def stub_out_db_instance_api(stubs):
else: else:
raise NotImplementedError() raise NotImplementedError()
def fake_instance_create(values): def fake_instance_create(context, values):
"""Stubs out the db.instance_create method.""" """Stubs out the db.instance_create method."""
type_data = INSTANCE_TYPES[values['instance_type']] type_data = INSTANCE_TYPES[values['instance_type']]

View File

@ -42,20 +42,6 @@ def stubout_instance_snapshot(stubs):
stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image) stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image)
def fake_wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref,
original_parent_uuid):
from nova.virt.xenapi.fake import create_vdi
name_label = "instance-%s" % instance_id
#TODO: create fake SR record
sr_ref = "fakesr"
vdi_ref = create_vdi(name_label=name_label, read_only=False,
sr_ref=sr_ref, sharable=False)
vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref)
vdi_uuid = vdi_rec['uuid']
return vdi_uuid
stubs.Set(vm_utils.VMHelper, 'fetch_image', fake_fetch_image)
def fake_parse_xmlrpc_value(val): def fake_parse_xmlrpc_value(val):
return val return val
@ -251,10 +237,10 @@ class FakeSessionForMigrationTests(fake.SessionBase):
def __init__(self, uri): def __init__(self, uri):
super(FakeSessionForMigrationTests, self).__init__(uri) super(FakeSessionForMigrationTests, self).__init__(uri)
def VDI_get_by_uuid(*args): def VDI_get_by_uuid(self, *args):
return 'hurr' return 'hurr'
def VDI_resize_online(*args): def VDI_resize_online(self, *args):
pass pass
def VM_start(self, _1, ref, _2, _3): def VM_start(self, _1, ref, _2, _3):

View File

@ -78,7 +78,7 @@ def WrapTwistedOptions(wrapped):
self._absorbParameters() self._absorbParameters()
self._absorbHandlers() self._absorbHandlers()
super(TwistedOptionsToFlags, self).__init__() wrapped.__init__(self)
def _absorbFlags(self): def _absorbFlags(self):
twistd_flags = [] twistd_flags = []
@ -163,12 +163,12 @@ def WrapTwistedOptions(wrapped):
def parseArgs(self, *args): def parseArgs(self, *args):
# TODO(termie): figure out a decent way of dealing with args # TODO(termie): figure out a decent way of dealing with args
#return #return
super(TwistedOptionsToFlags, self).parseArgs(*args) wrapped.parseArgs(self, *args)
def postOptions(self): def postOptions(self):
self._doHandlers() self._doHandlers()
super(TwistedOptionsToFlags, self).postOptions() wrapped.postOptions(self)
def __getitem__(self, key): def __getitem__(self, key):
key = key.replace('-', '_') key = key.replace('-', '_')

View File

@ -142,24 +142,26 @@ def execute(*cmd, **kwargs):
env = os.environ.copy() env = os.environ.copy()
if addl_env: if addl_env:
env.update(addl_env) env.update(addl_env)
_PIPE = subprocess.PIPE # pylint: disable=E1101
obj = subprocess.Popen(cmd, obj = subprocess.Popen(cmd,
stdin=subprocess.PIPE, stdin=_PIPE,
stdout=subprocess.PIPE, stdout=_PIPE,
stderr=subprocess.PIPE, stderr=_PIPE,
env=env) env=env)
result = None result = None
if process_input is not None: if process_input is not None:
result = obj.communicate(process_input) result = obj.communicate(process_input)
else: else:
result = obj.communicate() result = obj.communicate()
obj.stdin.close() obj.stdin.close() # pylint: disable=E1101
if obj.returncode: _returncode = obj.returncode # pylint: disable=E1101
LOG.debug(_('Result was %s') % obj.returncode) if _returncode:
LOG.debug(_('Result was %s') % _returncode)
if type(check_exit_code) == types.IntType \ if type(check_exit_code) == types.IntType \
and obj.returncode != check_exit_code: and _returncode != check_exit_code:
(stdout, stderr) = result (stdout, stderr) = result
raise exception.ProcessExecutionError( raise exception.ProcessExecutionError(
exit_code=obj.returncode, exit_code=_returncode,
stdout=stdout, stdout=stdout,
stderr=stderr, stderr=stderr,
cmd=' '.join(cmd)) cmd=' '.join(cmd))

View File

@ -23,6 +23,7 @@ Handling of VM disk images.
from nova import context from nova import context
from nova import flags from nova import flags
from nova.image import glance as glance_image_service
import nova.image import nova.image
from nova import log as logging from nova import log as logging
from nova import utils from nova import utils
@ -42,13 +43,3 @@ def fetch(image_href, path, _user, _project):
elevated = context.get_admin_context() elevated = context.get_admin_context()
metadata = image_service.get(elevated, image_id, image_file) metadata = image_service.get(elevated, image_id, image_file)
return metadata return metadata
# TODO(vish): xenapi should use the glance client code directly instead
# of retrieving the image using this method.
def image_url(image):
if FLAGS.image_service == "nova.image.glance.GlanceImageService":
return "http://%s:%s/images/%s" % (FLAGS.glance_host,
FLAGS.glance_port, image)
return "http://%s:%s/_images/%s/image" % (FLAGS.s3_host, FLAGS.s3_port,
image)

View File

@ -403,8 +403,7 @@ class LibvirtConnection(driver.ComputeDriver):
'is_public': False, 'is_public': False,
'status': 'active', 'status': 'active',
'name': snapshot['name'], 'name': snapshot['name'],
'properties': {'architecture': 'properties': {
base['properties']['architecture'],
'kernel_id': instance['kernel_id'], 'kernel_id': instance['kernel_id'],
'image_location': 'snapshot', 'image_location': 'snapshot',
'image_state': 'available', 'image_state': 'available',
@ -412,6 +411,9 @@ class LibvirtConnection(driver.ComputeDriver):
'ramdisk_id': instance['ramdisk_id'], 'ramdisk_id': instance['ramdisk_id'],
} }
} }
if 'architecture' in base['properties']:
arch = base['properties']['architecture']
metadata['properties']['architecture'] = arch
# Make the snapshot # Make the snapshot
snapshot_name = uuid.uuid4().hex snapshot_name = uuid.uuid4().hex

View File

@ -340,10 +340,6 @@ class SessionBase(object):
return return
db_ref['xenstore_data'][key] = None db_ref['xenstore_data'][key] = None
def network_get_all_records_where(self, _1, _2):
# TODO (salvatore-orlando): filter table on _2
return _db_content['network']
def VM_add_to_xenstore_data(self, _1, vm_ref, key, value): def VM_add_to_xenstore_data(self, _1, vm_ref, key, value):
db_ref = _db_content['VM'][vm_ref] db_ref = _db_content['VM'][vm_ref]
if not 'xenstore_data' in db_ref: if not 'xenstore_data' in db_ref:
@ -354,7 +350,7 @@ class SessionBase(object):
#Always return 12GB available #Always return 12GB available
return 12 * 1024 * 1024 * 1024 return 12 * 1024 * 1024 * 1024
def host_call_plugin(*args): def host_call_plugin(self, *args):
return 'herp' return 'herp'
def network_get_all_records_where(self, _1, filter): def network_get_all_records_where(self, _1, filter):

View File

@ -33,6 +33,7 @@ import glance.client
from nova import exception from nova import exception
from nova import flags from nova import flags
import nova.image import nova.image
from nova.image import glance as glance_image_service
from nova import log as logging from nova import log as logging
from nova import utils from nova import utils
from nova.auth.manager import AuthManager from nova.auth.manager import AuthManager
@ -358,10 +359,12 @@ class VMHelper(HelperBase):
os_type = instance.os_type or FLAGS.default_os_type os_type = instance.os_type or FLAGS.default_os_type
glance_host, glance_port = \
glance_image_service.pick_glance_api_server()
params = {'vdi_uuids': vdi_uuids, params = {'vdi_uuids': vdi_uuids,
'image_id': image_id, 'image_id': image_id,
'glance_host': FLAGS.glance_host, 'glance_host': glance_host,
'glance_port': FLAGS.glance_port, 'glance_port': glance_port,
'sr_path': cls.get_sr_path(session), 'sr_path': cls.get_sr_path(session),
'os_type': os_type} 'os_type': os_type}
@ -409,9 +412,11 @@ class VMHelper(HelperBase):
# here (under Python 2.6+) and pass them as arguments # here (under Python 2.6+) and pass them as arguments
uuid_stack = [str(uuid.uuid4()) for i in xrange(2)] uuid_stack = [str(uuid.uuid4()) for i in xrange(2)]
glance_host, glance_port = \
glance_image_service.pick_glance_api_server()
params = {'image_id': image, params = {'image_id': image,
'glance_host': FLAGS.glance_host, 'glance_host': glance_host,
'glance_port': FLAGS.glance_port, 'glance_port': glance_port,
'uuid_stack': uuid_stack, 'uuid_stack': uuid_stack,
'sr_path': cls.get_sr_path(session)} 'sr_path': cls.get_sr_path(session)}
@ -576,7 +581,8 @@ class VMHelper(HelperBase):
Returns: A single filename if image_type is KERNEL_RAMDISK Returns: A single filename if image_type is KERNEL_RAMDISK
A list of dictionaries that describe VDIs, otherwise A list of dictionaries that describe VDIs, otherwise
""" """
url = images.image_url(image) url = "http://%s:%s/_images/%s/image" % (FLAGS.s3_host, FLAGS.s3_port,
image)
LOG.debug(_("Asking xapi to fetch %(url)s as %(access)s") % locals()) LOG.debug(_("Asking xapi to fetch %(url)s as %(access)s") % locals())
if image_type == ImageType.KERNEL_RAMDISK: if image_type == ImageType.KERNEL_RAMDISK:
fn = 'get_kernel' fn = 'get_kernel'

View File

@ -101,7 +101,7 @@ class VMOps(object):
if not vm_ref: if not vm_ref:
vm_ref = VMHelper.lookup(self._session, instance.name) vm_ref = VMHelper.lookup(self._session, instance.name)
if vm_ref is None: if vm_ref is None:
raise exception(_('Attempted to power on non-existent instance' raise Exception(_('Attempted to power on non-existent instance'
' bad instance id %s') % instance.id) ' bad instance id %s') % instance.id)
LOG.debug(_("Starting instance %s"), instance.name) LOG.debug(_("Starting instance %s"), instance.name)
self._session.call_xenapi('VM.start', vm_ref, False, False) self._session.call_xenapi('VM.start', vm_ref, False, False)

View File

@ -67,14 +67,11 @@ function run_pep8 {
srcfiles=`find bin -type f ! -name "nova.conf*"` srcfiles=`find bin -type f ! -name "nova.conf*"`
srcfiles+=" `find tools/*`" srcfiles+=" `find tools/*`"
srcfiles+=" nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance" srcfiles+=" nova setup.py plugins/xenserver/xenapi/etc/xapi.d/plugins/glance"
pep8 --repeat --show-pep8 --show-source --exclude=vcsversion.py ${srcfiles} # Just run PEP8 in current environment
${wrapper} pep8 --repeat --show-pep8 --show-source \
--exclude=vcsversion.py ${srcfiles}
} }
if [ $just_pep8 -eq 1 ]; then
run_pep8
exit
fi
NOSETESTS="python run_tests.py $noseargs" NOSETESTS="python run_tests.py $noseargs"
if [ $never_venv -eq 0 ] if [ $never_venv -eq 0 ]
@ -103,6 +100,11 @@ then
fi fi
fi fi
if [ $just_pep8 -eq 1 ]; then
run_pep8
exit
fi
run_tests || exit run_tests || exit
# Also run pep8 if no options were provided. # Also run pep8 if no options were provided.