merging trunk
This commit is contained in:
2
Authors
2
Authors
@@ -27,8 +27,10 @@ Gabe Westmaas <gabe.westmaas@rackspace.com>
|
||||
Hisaharu Ishii <ishii.hisaharu@lab.ntt.co.jp>
|
||||
Hisaki Ohara <hisaki.ohara@intel.com>
|
||||
Ilya Alekseyev <ialekseev@griddynamics.com>
|
||||
Jason Koelker <jason@koelker.net>
|
||||
Jay Pipes <jaypipes@gmail.com>
|
||||
Jesse Andrews <anotherjesse@gmail.com>
|
||||
Jimmy Bergman <jimmy@sigint.se>
|
||||
Joe Heck <heckj@mac.com>
|
||||
Joel Moore <joelbm24@gmail.com>
|
||||
Johannes Erdfelt <johannes.erdfelt@rackspace.com>
|
||||
|
17
HACKING
17
HACKING
@@ -50,17 +50,24 @@ Human Alphabetical Order Examples
|
||||
|
||||
Docstrings
|
||||
----------
|
||||
"""Summary of the function, class or method, less than 80 characters.
|
||||
"""A one line docstring looks like this and ends in a period."""
|
||||
|
||||
New paragraph after newline that explains in more detail any general
|
||||
information about the function, class or method. After this, if defining
|
||||
parameters and return types use the Sphinx format. After that an extra
|
||||
newline then close the quotations.
|
||||
|
||||
"""A multiline docstring has a one-line summary, less than 80 characters.
|
||||
|
||||
Then a new paragraph after a newline that explains in more detail any
|
||||
general information about the function, class or method. Example usages
|
||||
are also great to have here if it is a complex class for function. After
|
||||
you have finished your descriptions add an extra newline and close the
|
||||
quotations.
|
||||
|
||||
When writing the docstring for a class, an extra line should be placed
|
||||
after the closing quotations. For more in-depth explanations for these
|
||||
decisions see http://www.python.org/dev/peps/pep-0257/
|
||||
|
||||
If you are going to describe parameters and return values, use Sphinx, the
|
||||
appropriate syntax is as follows.
|
||||
|
||||
:param foo: the foo parameter
|
||||
:param bar: the bar parameter
|
||||
:returns: description of the return value
|
||||
|
@@ -28,11 +28,11 @@ import sys
|
||||
|
||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, POSSIBLE_TOPDIR)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
|
@@ -58,7 +58,6 @@ import gettext
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
@@ -66,11 +65,11 @@ import IPy
|
||||
|
||||
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, POSSIBLE_TOPDIR)
|
||||
|
||||
gettext.install('nova', unicode=1)
|
||||
|
||||
@@ -83,6 +82,7 @@ from nova import log as logging
|
||||
from nova import quota
|
||||
from nova import rpc
|
||||
from nova import utils
|
||||
from nova import version
|
||||
from nova.api.ec2 import ec2utils
|
||||
from nova.auth import manager
|
||||
from nova.cloudpipe import pipelib
|
||||
@@ -386,10 +386,10 @@ class ProjectCommands(object):
|
||||
with open(filename, 'w') as f:
|
||||
f.write(rc)
|
||||
|
||||
def list(self):
|
||||
def list(self, username=None):
|
||||
"""Lists all projects
|
||||
arguments: <none>"""
|
||||
for project in self.manager.get_projects():
|
||||
arguments: [username]"""
|
||||
for project in self.manager.get_projects(username):
|
||||
print project.name
|
||||
|
||||
def quota(self, project_id, key=None, value=None):
|
||||
@@ -449,7 +449,7 @@ class FixedIpCommands(object):
|
||||
ctxt = context.get_admin_context()
|
||||
|
||||
try:
|
||||
if host == None:
|
||||
if host is None:
|
||||
fixed_ips = db.fixed_ip_get_all(ctxt)
|
||||
else:
|
||||
fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)
|
||||
@@ -499,7 +499,7 @@ class FloatingIpCommands(object):
|
||||
"""Lists all floating ips (optionally by host)
|
||||
arguments: [host]"""
|
||||
ctxt = context.get_admin_context()
|
||||
if host == None:
|
||||
if host is None:
|
||||
floating_ips = db.floating_ip_get_all(ctxt)
|
||||
else:
|
||||
floating_ips = db.floating_ip_get_all_by_host(ctxt, host)
|
||||
@@ -591,7 +591,7 @@ class VmCommands(object):
|
||||
_('zone'),
|
||||
_('index'))
|
||||
|
||||
if host == None:
|
||||
if host is None:
|
||||
instances = db.instance_get_all(context.get_admin_context())
|
||||
else:
|
||||
instances = db.instance_get_all_by_host(
|
||||
@@ -759,6 +759,17 @@ class DbCommands(object):
|
||||
print migration.db_version()
|
||||
|
||||
|
||||
class VersionCommands(object):
|
||||
"""Class for exposing the codebase version."""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def list(self):
|
||||
print _("%s (%s)") %\
|
||||
(version.version_string(), version.version_string_with_vcs())
|
||||
|
||||
|
||||
class VolumeCommands(object):
|
||||
"""Methods for dealing with a cloud in an odd state"""
|
||||
|
||||
@@ -809,11 +820,11 @@ class VolumeCommands(object):
|
||||
class InstanceTypeCommands(object):
|
||||
"""Class for managing instance types / flavors."""
|
||||
|
||||
def _print_instance_types(self, n, val):
|
||||
def _print_instance_types(self, name, val):
|
||||
deleted = ('', ', inactive')[val["deleted"] == 1]
|
||||
print ("%s: Memory: %sMB, VCPUS: %s, Storage: %sGB, FlavorID: %s, "
|
||||
"Swap: %sGB, RXTX Quota: %sGB, RXTX Cap: %sMB%s") % (
|
||||
n, val["memory_mb"], val["vcpus"], val["local_gb"],
|
||||
name, val["memory_mb"], val["vcpus"], val["local_gb"],
|
||||
val["flavorid"], val["swap"], val["rxtx_quota"],
|
||||
val["rxtx_cap"], deleted)
|
||||
|
||||
@@ -827,11 +838,17 @@ class InstanceTypeCommands(object):
|
||||
instance_types.create(name, memory, vcpus, local_gb,
|
||||
flavorid, swap, rxtx_quota, rxtx_cap)
|
||||
except exception.InvalidInputException:
|
||||
print "Must supply valid parameters to create instance type"
|
||||
print "Must supply valid parameters to create instance_type"
|
||||
print e
|
||||
sys.exit(1)
|
||||
except exception.DBError, e:
|
||||
print "DB Error: %s" % e
|
||||
except exception.ApiError, e:
|
||||
print "\n\n"
|
||||
print "\n%s" % e
|
||||
print "Please ensure instance_type name and flavorid are unique."
|
||||
print "To complete remove a instance_type, use the --purge flag:"
|
||||
print "\n # nova-manage instance_type delete <name> --purge\n"
|
||||
print "Currently defined instance_type names and flavorids:"
|
||||
self.list("--all")
|
||||
sys.exit(2)
|
||||
except:
|
||||
print "Unknown error"
|
||||
@@ -864,7 +881,7 @@ class InstanceTypeCommands(object):
|
||||
"""Lists all active or specific instance types / flavors
|
||||
arguments: [name]"""
|
||||
try:
|
||||
if name == None:
|
||||
if name is None:
|
||||
inst_types = instance_types.get_all_types()
|
||||
elif name == "--all":
|
||||
inst_types = instance_types.get_all_types(True)
|
||||
@@ -1009,7 +1026,7 @@ class ImageCommands(object):
|
||||
if (FLAGS.image_service == 'nova.image.local.LocalImageService'
|
||||
and directory == os.path.abspath(FLAGS.images_path)):
|
||||
new_dir = "%s_bak" % directory
|
||||
os.move(directory, new_dir)
|
||||
os.rename(directory, new_dir)
|
||||
os.mkdir(directory)
|
||||
directory = new_dir
|
||||
for fn in glob.glob("%s/*/info.json" % directory):
|
||||
@@ -1021,7 +1038,7 @@ class ImageCommands(object):
|
||||
machine_images[image_path] = image_metadata
|
||||
else:
|
||||
other_images[image_path] = image_metadata
|
||||
except Exception as exc:
|
||||
except Exception:
|
||||
print _("Failed to load %(fn)s.") % locals()
|
||||
# NOTE(vish): do kernels and ramdisks first so images
|
||||
self._convert_images(other_images)
|
||||
@@ -1044,7 +1061,8 @@ CATEGORIES = [
|
||||
('volume', VolumeCommands),
|
||||
('instance_type', InstanceTypeCommands),
|
||||
('image', ImageCommands),
|
||||
('flavor', InstanceTypeCommands)]
|
||||
('flavor', InstanceTypeCommands),
|
||||
('version', VersionCommands)]
|
||||
|
||||
|
||||
def lazy_match(name, key_value_tuples):
|
||||
@@ -1086,6 +1104,8 @@ def main():
|
||||
|
||||
script_name = argv.pop(0)
|
||||
if len(argv) < 1:
|
||||
print _("\nOpenStack Nova version: %s (%s)\n") %\
|
||||
(version.version_string(), version.version_string_with_vcs())
|
||||
print script_name + " category action [<args>]"
|
||||
print _("Available categories:")
|
||||
for k, _v in CATEGORIES:
|
||||
|
@@ -81,7 +81,7 @@ class DbDriver(object):
|
||||
user_ref = db.user_create(context.get_admin_context(), values)
|
||||
return self._db_user_to_auth_user(user_ref)
|
||||
except exception.Duplicate, e:
|
||||
raise exception.Duplicate(_('User %s already exists') % name)
|
||||
raise exception.UserExists(user=name)
|
||||
|
||||
def _db_user_to_auth_user(self, user_ref):
|
||||
return {'id': user_ref['id'],
|
||||
@@ -103,9 +103,7 @@ class DbDriver(object):
|
||||
"""Create a project"""
|
||||
manager = db.user_get(context.get_admin_context(), manager_uid)
|
||||
if not manager:
|
||||
raise exception.NotFound(_("Project can't be created because "
|
||||
"manager %s doesn't exist")
|
||||
% manager_uid)
|
||||
raise exception.UserNotFound(user_id=manager_uid)
|
||||
|
||||
# description is a required attribute
|
||||
if description is None:
|
||||
@@ -115,13 +113,11 @@ class DbDriver(object):
|
||||
# on to create the project. This way we won't have to destroy
|
||||
# the project again because a user turns out to be invalid.
|
||||
members = set([manager])
|
||||
if member_uids != None:
|
||||
if member_uids is not None:
|
||||
for member_uid in member_uids:
|
||||
member = db.user_get(context.get_admin_context(), member_uid)
|
||||
if not member:
|
||||
raise exception.NotFound(_("Project can't be created "
|
||||
"because user %s doesn't exist")
|
||||
% member_uid)
|
||||
raise exception.UserNotFound(user_id=member_uid)
|
||||
members.add(member)
|
||||
|
||||
values = {'id': name,
|
||||
@@ -132,8 +128,7 @@ class DbDriver(object):
|
||||
try:
|
||||
project = db.project_create(context.get_admin_context(), values)
|
||||
except exception.Duplicate:
|
||||
raise exception.Duplicate(_("Project can't be created because "
|
||||
"project %s already exists") % name)
|
||||
raise exception.ProjectExists(project=name)
|
||||
|
||||
for member in members:
|
||||
db.project_add_member(context.get_admin_context(),
|
||||
@@ -154,9 +149,7 @@ class DbDriver(object):
|
||||
if manager_uid:
|
||||
manager = db.user_get(context.get_admin_context(), manager_uid)
|
||||
if not manager:
|
||||
raise exception.NotFound(_("Project can't be modified because "
|
||||
"manager %s doesn't exist") %
|
||||
manager_uid)
|
||||
raise exception.UserNotFound(user_id=manager_uid)
|
||||
values['project_manager'] = manager['id']
|
||||
if description:
|
||||
values['description'] = description
|
||||
@@ -244,8 +237,8 @@ class DbDriver(object):
|
||||
def _validate_user_and_project(self, user_id, project_id):
|
||||
user = db.user_get(context.get_admin_context(), user_id)
|
||||
if not user:
|
||||
raise exception.NotFound(_('User "%s" not found') % user_id)
|
||||
raise exception.UserNotFound(user_id=user_id)
|
||||
project = db.project_get(context.get_admin_context(), project_id)
|
||||
if not project:
|
||||
raise exception.NotFound(_('Project "%s" not found') % project_id)
|
||||
raise exception.ProjectNotFound(project_id=project_id)
|
||||
return user, project
|
||||
|
@@ -171,7 +171,7 @@ class LdapDriver(object):
|
||||
def create_user(self, name, access_key, secret_key, is_admin):
|
||||
"""Create a user"""
|
||||
if self.__user_exists(name):
|
||||
raise exception.Duplicate(_("LDAP user %s already exists") % name)
|
||||
raise exception.LDAPUserExists(user=name)
|
||||
if FLAGS.ldap_user_modify_only:
|
||||
if self.__ldap_user_exists(name):
|
||||
# Retrieve user by name
|
||||
@@ -202,8 +202,7 @@ class LdapDriver(object):
|
||||
self.conn.modify_s(self.__uid_to_dn(name), attr)
|
||||
return self.get_user(name)
|
||||
else:
|
||||
raise exception.NotFound(_("LDAP object for %s doesn't exist")
|
||||
% name)
|
||||
raise exception.LDAPUserNotFound(user_id=name)
|
||||
else:
|
||||
attr = [
|
||||
('objectclass', ['person',
|
||||
@@ -226,12 +225,9 @@ class LdapDriver(object):
|
||||
description=None, member_uids=None):
|
||||
"""Create a project"""
|
||||
if self.__project_exists(name):
|
||||
raise exception.Duplicate(_("Project can't be created because "
|
||||
"project %s already exists") % name)
|
||||
raise exception.ProjectExists(project=name)
|
||||
if not self.__user_exists(manager_uid):
|
||||
raise exception.NotFound(_("Project can't be created because "
|
||||
"manager %s doesn't exist")
|
||||
% manager_uid)
|
||||
raise exception.LDAPUserNotFound(user_id=manager_uid)
|
||||
manager_dn = self.__uid_to_dn(manager_uid)
|
||||
# description is a required attribute
|
||||
if description is None:
|
||||
@@ -240,9 +236,7 @@ class LdapDriver(object):
|
||||
if member_uids is not None:
|
||||
for member_uid in member_uids:
|
||||
if not self.__user_exists(member_uid):
|
||||
raise exception.NotFound(_("Project can't be created "
|
||||
"because user %s doesn't exist")
|
||||
% member_uid)
|
||||
raise exception.LDAPUserNotFound(user_id=member_uid)
|
||||
members.append(self.__uid_to_dn(member_uid))
|
||||
# always add the manager as a member because members is required
|
||||
if not manager_dn in members:
|
||||
@@ -265,9 +259,7 @@ class LdapDriver(object):
|
||||
attr = []
|
||||
if manager_uid:
|
||||
if not self.__user_exists(manager_uid):
|
||||
raise exception.NotFound(_("Project can't be modified because "
|
||||
"manager %s doesn't exist")
|
||||
% manager_uid)
|
||||
raise exception.LDAPUserNotFound(user_id=manager_uid)
|
||||
manager_dn = self.__uid_to_dn(manager_uid)
|
||||
attr.append((self.ldap.MOD_REPLACE, LdapDriver.project_attribute,
|
||||
manager_dn))
|
||||
@@ -347,7 +339,7 @@ class LdapDriver(object):
|
||||
def delete_user(self, uid):
|
||||
"""Delete a user"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound(_("User %s doesn't exist") % uid)
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
self.__remove_from_all(uid)
|
||||
if FLAGS.ldap_user_modify_only:
|
||||
# Delete attributes
|
||||
@@ -471,15 +463,12 @@ class LdapDriver(object):
|
||||
description, member_uids=None):
|
||||
"""Create a group"""
|
||||
if self.__group_exists(group_dn):
|
||||
raise exception.Duplicate(_("Group can't be created because "
|
||||
"group %s already exists") % name)
|
||||
raise exception.LDAPGroupExists(group=name)
|
||||
members = []
|
||||
if member_uids is not None:
|
||||
for member_uid in member_uids:
|
||||
if not self.__user_exists(member_uid):
|
||||
raise exception.NotFound(_("Group can't be created "
|
||||
"because user %s doesn't exist")
|
||||
% member_uid)
|
||||
raise exception.LDAPUserNotFound(user_id=member_uid)
|
||||
members.append(self.__uid_to_dn(member_uid))
|
||||
dn = self.__uid_to_dn(uid)
|
||||
if not dn in members:
|
||||
@@ -494,8 +483,7 @@ class LdapDriver(object):
|
||||
def __is_in_group(self, uid, group_dn):
|
||||
"""Check if user is in group"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound(_("User %s can't be searched in group "
|
||||
"because the user doesn't exist") % uid)
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
if not self.__group_exists(group_dn):
|
||||
return False
|
||||
res = self.__find_object(group_dn,
|
||||
@@ -506,29 +494,23 @@ class LdapDriver(object):
|
||||
def __add_to_group(self, uid, group_dn):
|
||||
"""Add user to group"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound(_("User %s can't be added to the group "
|
||||
"because the user doesn't exist") % uid)
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
if not self.__group_exists(group_dn):
|
||||
raise exception.NotFound(_("The group at dn %s doesn't exist") %
|
||||
group_dn)
|
||||
raise exception.LDAPGroupNotFound(group_id=group_dn)
|
||||
if self.__is_in_group(uid, group_dn):
|
||||
raise exception.Duplicate(_("User %(uid)s is already a member of "
|
||||
"the group %(group_dn)s") % locals())
|
||||
raise exception.LDAPMembershipExists(uid=uid, group_dn=group_dn)
|
||||
attr = [(self.ldap.MOD_ADD, 'member', self.__uid_to_dn(uid))]
|
||||
self.conn.modify_s(group_dn, attr)
|
||||
|
||||
def __remove_from_group(self, uid, group_dn):
|
||||
"""Remove user from group"""
|
||||
if not self.__group_exists(group_dn):
|
||||
raise exception.NotFound(_("The group at dn %s doesn't exist")
|
||||
% group_dn)
|
||||
raise exception.LDAPGroupNotFound(group_id=group_dn)
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound(_("User %s can't be removed from the "
|
||||
"group because the user doesn't exist")
|
||||
% uid)
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
if not self.__is_in_group(uid, group_dn):
|
||||
raise exception.NotFound(_("User %s is not a member of the group")
|
||||
% uid)
|
||||
raise exception.LDAPGroupMembershipNotFound(user_id=uid,
|
||||
group_id=group_dn)
|
||||
# NOTE(vish): remove user from group and any sub_groups
|
||||
sub_dns = self.__find_group_dns_with_member(group_dn, uid)
|
||||
for sub_dn in sub_dns:
|
||||
@@ -548,9 +530,7 @@ class LdapDriver(object):
|
||||
def __remove_from_all(self, uid):
|
||||
"""Remove user from all roles and projects"""
|
||||
if not self.__user_exists(uid):
|
||||
raise exception.NotFound(_("User %s can't be removed from all "
|
||||
"because the user doesn't exist")
|
||||
% uid)
|
||||
raise exception.LDAPUserNotFound(user_id=uid)
|
||||
role_dns = self.__find_group_dns_with_member(
|
||||
FLAGS.role_project_subtree, uid)
|
||||
for role_dn in role_dns:
|
||||
@@ -563,8 +543,7 @@ class LdapDriver(object):
|
||||
def __delete_group(self, group_dn):
|
||||
"""Delete Group"""
|
||||
if not self.__group_exists(group_dn):
|
||||
raise exception.NotFound(_("Group at dn %s doesn't exist")
|
||||
% group_dn)
|
||||
raise exception.LDAPGroupNotFound(group_id=group_dn)
|
||||
self.conn.delete_s(group_dn)
|
||||
|
||||
def __delete_roles(self, project_dn):
|
||||
|
@@ -223,6 +223,13 @@ class AuthManager(object):
|
||||
if driver or not getattr(self, 'driver', None):
|
||||
self.driver = utils.import_class(driver or FLAGS.auth_driver)
|
||||
|
||||
if FLAGS.memcached_servers:
|
||||
import memcache
|
||||
else:
|
||||
from nova import fakememcache as memcache
|
||||
self.mc = memcache.Client(FLAGS.memcached_servers,
|
||||
debug=0)
|
||||
|
||||
def authenticate(self, access, signature, params, verb='GET',
|
||||
server_string='127.0.0.1:8773', path='/',
|
||||
check_type='ec2', headers=None):
|
||||
@@ -268,10 +275,9 @@ class AuthManager(object):
|
||||
LOG.debug(_('Looking up user: %r'), access_key)
|
||||
user = self.get_user_from_access_key(access_key)
|
||||
LOG.debug('user: %r', user)
|
||||
if user == None:
|
||||
if user is None:
|
||||
LOG.audit(_("Failed authorization for access key %s"), access_key)
|
||||
raise exception.NotFound(_('No user found for access key %s')
|
||||
% access_key)
|
||||
raise exception.AccessKeyNotFound(access_key=access_key)
|
||||
|
||||
# NOTE(vish): if we stop using project name as id we need better
|
||||
# logic to find a default project for user
|
||||
@@ -280,13 +286,12 @@ class AuthManager(object):
|
||||
project_id = user.name
|
||||
|
||||
project = self.get_project(project_id)
|
||||
if project == None:
|
||||
if project is None:
|
||||
pjid = project_id
|
||||
uname = user.name
|
||||
LOG.audit(_("failed authorization: no project named %(pjid)s"
|
||||
" (user=%(uname)s)") % locals())
|
||||
raise exception.NotFound(_('No project called %s could be found')
|
||||
% project_id)
|
||||
raise exception.ProjectNotFound(project_id=project_id)
|
||||
if not self.is_admin(user) and not self.is_project_member(user,
|
||||
project):
|
||||
uname = user.name
|
||||
@@ -295,8 +300,8 @@ class AuthManager(object):
|
||||
pjid = project.id
|
||||
LOG.audit(_("Failed authorization: user %(uname)s not admin"
|
||||
" and not member of project %(pjname)s") % locals())
|
||||
raise exception.NotFound(_('User %(uid)s is not a member of'
|
||||
' project %(pjid)s') % locals())
|
||||
raise exception.ProjectMembershipNotFound(project_id=pjid,
|
||||
user_id=uid)
|
||||
if check_type == 's3':
|
||||
sign = signer.Signer(user.secret.encode())
|
||||
expected_signature = sign.s3_authorization(headers, verb, path)
|
||||
@@ -305,7 +310,8 @@ class AuthManager(object):
|
||||
LOG.debug('signature: %s', signature)
|
||||
if signature != expected_signature:
|
||||
LOG.audit(_("Invalid signature for user %s"), user.name)
|
||||
raise exception.NotAuthorized(_('Signature does not match'))
|
||||
raise exception.InvalidSignature(signature=signature,
|
||||
user=user)
|
||||
elif check_type == 'ec2':
|
||||
# NOTE(vish): hmac can't handle unicode, so encode ensures that
|
||||
# secret isn't unicode
|
||||
@@ -316,7 +322,8 @@ class AuthManager(object):
|
||||
LOG.debug('signature: %s', signature)
|
||||
if signature != expected_signature:
|
||||
LOG.audit(_("Invalid signature for user %s"), user.name)
|
||||
raise exception.NotAuthorized(_('Signature does not match'))
|
||||
raise exception.InvalidSignature(signature=signature,
|
||||
user=user)
|
||||
return (user, project)
|
||||
|
||||
def get_access_key(self, user, project):
|
||||
@@ -360,6 +367,27 @@ class AuthManager(object):
|
||||
if self.has_role(user, role):
|
||||
return True
|
||||
|
||||
def _build_mc_key(self, user, role, project=None):
|
||||
key_parts = ['rolecache', User.safe_id(user), str(role)]
|
||||
if project:
|
||||
key_parts.append(Project.safe_id(project))
|
||||
return '-'.join(key_parts)
|
||||
|
||||
def _clear_mc_key(self, user, role, project=None):
|
||||
# NOTE(anthony): it would be better to delete the key
|
||||
self.mc.set(self._build_mc_key(user, role, project), None)
|
||||
|
||||
def _has_role(self, user, role, project=None):
|
||||
mc_key = self._build_mc_key(user, role, project)
|
||||
rslt = self.mc.get(mc_key)
|
||||
if rslt is None:
|
||||
with self.driver() as drv:
|
||||
rslt = drv.has_role(user, role, project)
|
||||
self.mc.set(mc_key, rslt)
|
||||
return rslt
|
||||
else:
|
||||
return rslt
|
||||
|
||||
def has_role(self, user, role, project=None):
|
||||
"""Checks existence of role for user
|
||||
|
||||
@@ -383,24 +411,24 @@ class AuthManager(object):
|
||||
@rtype: bool
|
||||
@return: True if the user has the role.
|
||||
"""
|
||||
with self.driver() as drv:
|
||||
if role == 'projectmanager':
|
||||
if not project:
|
||||
raise exception.Error(_("Must specify project"))
|
||||
return self.is_project_manager(user, project)
|
||||
if role == 'projectmanager':
|
||||
if not project:
|
||||
raise exception.Error(_("Must specify project"))
|
||||
return self.is_project_manager(user, project)
|
||||
|
||||
global_role = drv.has_role(User.safe_id(user),
|
||||
role,
|
||||
None)
|
||||
if not global_role:
|
||||
return global_role
|
||||
global_role = self._has_role(User.safe_id(user),
|
||||
role,
|
||||
None)
|
||||
|
||||
if not project or role in FLAGS.global_roles:
|
||||
return global_role
|
||||
if not global_role:
|
||||
return global_role
|
||||
|
||||
return drv.has_role(User.safe_id(user),
|
||||
role,
|
||||
Project.safe_id(project))
|
||||
if not project or role in FLAGS.global_roles:
|
||||
return global_role
|
||||
|
||||
return self._has_role(User.safe_id(user),
|
||||
role,
|
||||
Project.safe_id(project))
|
||||
|
||||
def add_role(self, user, role, project=None):
|
||||
"""Adds role for user
|
||||
@@ -420,9 +448,9 @@ class AuthManager(object):
|
||||
@param project: Project in which to add local role.
|
||||
"""
|
||||
if role not in FLAGS.allowed_roles:
|
||||
raise exception.NotFound(_("The %s role can not be found") % role)
|
||||
raise exception.UserRoleNotFound(role_id=role)
|
||||
if project is not None and role in FLAGS.global_roles:
|
||||
raise exception.NotFound(_("The %s role is global only") % role)
|
||||
raise exception.GlobalRoleNotAllowed(role_id=role)
|
||||
uid = User.safe_id(user)
|
||||
pid = Project.safe_id(project)
|
||||
if project:
|
||||
@@ -432,6 +460,7 @@ class AuthManager(object):
|
||||
LOG.audit(_("Adding sitewide role %(role)s to user %(uid)s")
|
||||
% locals())
|
||||
with self.driver() as drv:
|
||||
self._clear_mc_key(uid, role, pid)
|
||||
drv.add_role(uid, role, pid)
|
||||
|
||||
def remove_role(self, user, role, project=None):
|
||||
@@ -460,6 +489,7 @@ class AuthManager(object):
|
||||
LOG.audit(_("Removing sitewide role %(role)s"
|
||||
" from user %(uid)s") % locals())
|
||||
with self.driver() as drv:
|
||||
self._clear_mc_key(uid, role, pid)
|
||||
drv.remove_role(uid, role, pid)
|
||||
|
||||
@staticmethod
|
||||
@@ -646,9 +676,9 @@ class AuthManager(object):
|
||||
@rtype: User
|
||||
@return: The new user.
|
||||
"""
|
||||
if access == None:
|
||||
if access is None:
|
||||
access = str(uuid.uuid4())
|
||||
if secret == None:
|
||||
if secret is None:
|
||||
secret = str(uuid.uuid4())
|
||||
with self.driver() as drv:
|
||||
user_dict = drv.create_user(name, access, secret, admin)
|
||||
|
@@ -18,14 +18,14 @@
|
||||
|
||||
"""Super simple fake memcache client."""
|
||||
|
||||
import utils
|
||||
from nova import utils
|
||||
|
||||
|
||||
class Client(object):
|
||||
"""Replicates a tiny subset of memcached client interface."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Ignores the passed in args"""
|
||||
"""Ignores the passed in args."""
|
||||
self.cache = {}
|
||||
|
||||
def get(self, key):
|
||||
|
@@ -16,9 +16,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
"""Command-line flag library.
|
||||
|
||||
Wraps gflags.
|
||||
|
||||
Package-level global flags are defined here, the rest are defined
|
||||
where they're used.
|
||||
|
||||
"""
|
||||
|
||||
import getopt
|
||||
@@ -145,10 +149,12 @@ class FlagValues(gflags.FlagValues):
|
||||
|
||||
|
||||
class StrWrapper(object):
|
||||
"""Wrapper around FlagValues objects
|
||||
"""Wrapper around FlagValues objects.
|
||||
|
||||
Wraps FlagValues objects for string.Template so that we're
|
||||
sure to return strings."""
|
||||
sure to return strings.
|
||||
|
||||
"""
|
||||
def __init__(self, context_objs):
|
||||
self.context_objs = context_objs
|
||||
|
||||
@@ -169,6 +175,7 @@ def _GetCallingModule():
|
||||
|
||||
We generally use this function to get the name of the module calling a
|
||||
DEFINE_foo... function.
|
||||
|
||||
"""
|
||||
# Walk down the stack to find the first globals dict that's not ours.
|
||||
for depth in range(1, sys.getrecursionlimit()):
|
||||
@@ -192,6 +199,7 @@ def __GetModuleName(globals_dict):
|
||||
Returns:
|
||||
A string (the name of the module) or None (if the module could not
|
||||
be identified.
|
||||
|
||||
"""
|
||||
for name, module in sys.modules.iteritems():
|
||||
if getattr(module, '__dict__', None) is globals_dict:
|
||||
@@ -316,7 +324,7 @@ DEFINE_string('null_kernel', 'nokernel',
|
||||
'kernel image that indicates not to use a kernel,'
|
||||
' but to use a raw disk image instead')
|
||||
|
||||
DEFINE_string('vpn_image_id', 'ami-cloudpipe', 'AMI for cloudpipe vpn server')
|
||||
DEFINE_integer('vpn_image_id', 0, 'integer id for cloudpipe vpn server')
|
||||
DEFINE_string('vpn_key_suffix',
|
||||
'-vpn',
|
||||
'Suffix to add to project name for vpn key and secgroups')
|
||||
@@ -326,7 +334,7 @@ DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger')
|
||||
DEFINE_string('state_path', os.path.join(os.path.dirname(__file__), '../'),
|
||||
"Top-level directory for maintaining nova's state")
|
||||
DEFINE_string('lock_path', os.path.join(os.path.dirname(__file__), '../'),
|
||||
"Directory for lock files")
|
||||
'Directory for lock files')
|
||||
DEFINE_string('logdir', None, 'output to a per-service log file in named '
|
||||
'directory')
|
||||
|
||||
@@ -361,6 +369,9 @@ DEFINE_string('host', socket.gethostname(),
|
||||
DEFINE_string('node_availability_zone', 'nova',
|
||||
'availability zone of this node')
|
||||
|
||||
DEFINE_list('memcached_servers', None,
|
||||
'Memcached servers or None for in process cache.')
|
||||
|
||||
DEFINE_string('zone_name', 'nova', 'name of this zone')
|
||||
DEFINE_list('zone_capabilities',
|
||||
['hypervisor=xenserver;kvm', 'os=linux;windows'],
|
||||
|
51
nova/log.py
51
nova/log.py
@@ -16,16 +16,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Nova logging handler.
|
||||
"""Nova logging handler.
|
||||
|
||||
This module adds to logging functionality by adding the option to specify
|
||||
a context object when calling the various log methods. If the context object
|
||||
is not specified, default formatting is used.
|
||||
|
||||
It also allows setting of formatting information through flags.
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
import cStringIO
|
||||
import inspect
|
||||
@@ -41,34 +40,28 @@ from nova import version
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
flags.DEFINE_string('logging_context_format_string',
|
||||
'%(asctime)s %(levelname)s %(name)s '
|
||||
'[%(request_id)s %(user)s '
|
||||
'%(project)s] %(message)s',
|
||||
'format string to use for log messages with context')
|
||||
|
||||
flags.DEFINE_string('logging_default_format_string',
|
||||
'%(asctime)s %(levelname)s %(name)s [-] '
|
||||
'%(message)s',
|
||||
'format string to use for log messages without context')
|
||||
|
||||
flags.DEFINE_string('logging_debug_format_suffix',
|
||||
'from (pid=%(process)d) %(funcName)s'
|
||||
' %(pathname)s:%(lineno)d',
|
||||
'data to append to log format when level is DEBUG')
|
||||
|
||||
flags.DEFINE_string('logging_exception_prefix',
|
||||
'(%(name)s): TRACE: ',
|
||||
'prefix each line of exception output with this format')
|
||||
|
||||
flags.DEFINE_list('default_log_levels',
|
||||
['amqplib=WARN',
|
||||
'sqlalchemy=WARN',
|
||||
'boto=WARN',
|
||||
'eventlet.wsgi.server=WARN'],
|
||||
'list of logger=LEVEL pairs')
|
||||
|
||||
flags.DEFINE_bool('use_syslog', False, 'output to syslog')
|
||||
flags.DEFINE_string('logfile', None, 'output to named file')
|
||||
|
||||
@@ -83,6 +76,8 @@ WARN = logging.WARN
|
||||
INFO = logging.INFO
|
||||
DEBUG = logging.DEBUG
|
||||
NOTSET = logging.NOTSET
|
||||
|
||||
|
||||
# methods
|
||||
getLogger = logging.getLogger
|
||||
debug = logging.debug
|
||||
@@ -93,6 +88,8 @@ error = logging.error
|
||||
exception = logging.exception
|
||||
critical = logging.critical
|
||||
log = logging.log
|
||||
|
||||
|
||||
# handlers
|
||||
StreamHandler = logging.StreamHandler
|
||||
WatchedFileHandler = logging.handlers.WatchedFileHandler
|
||||
@@ -106,7 +103,7 @@ logging.addLevelName(AUDIT, 'AUDIT')
|
||||
|
||||
|
||||
def _dictify_context(context):
|
||||
if context == None:
|
||||
if context is None:
|
||||
return None
|
||||
if not isinstance(context, dict) \
|
||||
and getattr(context, 'to_dict', None):
|
||||
@@ -127,17 +124,18 @@ def _get_log_file_path(binary=None):
|
||||
|
||||
|
||||
class NovaLogger(logging.Logger):
|
||||
"""
|
||||
NovaLogger manages request context and formatting.
|
||||
"""NovaLogger manages request context and formatting.
|
||||
|
||||
This becomes the class that is instanciated by logging.getLogger.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, level=NOTSET):
|
||||
logging.Logger.__init__(self, name, level)
|
||||
self.setup_from_flags()
|
||||
|
||||
def setup_from_flags(self):
|
||||
"""Setup logger from flags"""
|
||||
"""Setup logger from flags."""
|
||||
level = NOTSET
|
||||
for pair in FLAGS.default_log_levels:
|
||||
logger, _sep, level_name = pair.partition('=')
|
||||
@@ -148,7 +146,7 @@ class NovaLogger(logging.Logger):
|
||||
self.setLevel(level)
|
||||
|
||||
def _log(self, level, msg, args, exc_info=None, extra=None, context=None):
|
||||
"""Extract context from any log call"""
|
||||
"""Extract context from any log call."""
|
||||
if not extra:
|
||||
extra = {}
|
||||
if context:
|
||||
@@ -157,17 +155,17 @@ class NovaLogger(logging.Logger):
|
||||
return logging.Logger._log(self, level, msg, args, exc_info, extra)
|
||||
|
||||
def addHandler(self, handler):
|
||||
"""Each handler gets our custom formatter"""
|
||||
"""Each handler gets our custom formatter."""
|
||||
handler.setFormatter(_formatter)
|
||||
return logging.Logger.addHandler(self, handler)
|
||||
|
||||
def audit(self, msg, *args, **kwargs):
|
||||
"""Shortcut for our AUDIT level"""
|
||||
"""Shortcut for our AUDIT level."""
|
||||
if self.isEnabledFor(AUDIT):
|
||||
self._log(AUDIT, msg, args, **kwargs)
|
||||
|
||||
def exception(self, msg, *args, **kwargs):
|
||||
"""Logging.exception doesn't handle kwargs, so breaks context"""
|
||||
"""Logging.exception doesn't handle kwargs, so breaks context."""
|
||||
if not kwargs.get('exc_info'):
|
||||
kwargs['exc_info'] = 1
|
||||
self.error(msg, *args, **kwargs)
|
||||
@@ -181,14 +179,13 @@ class NovaLogger(logging.Logger):
|
||||
for k in env.keys():
|
||||
if not isinstance(env[k], str):
|
||||
env.pop(k)
|
||||
message = "Environment: %s" % json.dumps(env)
|
||||
message = 'Environment: %s' % json.dumps(env)
|
||||
kwargs.pop('exc_info')
|
||||
self.error(message, **kwargs)
|
||||
|
||||
|
||||
class NovaFormatter(logging.Formatter):
|
||||
"""
|
||||
A nova.context.RequestContext aware formatter configured through flags.
|
||||
"""A nova.context.RequestContext aware formatter configured through flags.
|
||||
|
||||
The flags used to set format strings are: logging_context_foramt_string
|
||||
and logging_default_format_string. You can also specify
|
||||
@@ -197,10 +194,11 @@ class NovaFormatter(logging.Formatter):
|
||||
|
||||
For information about what variables are available for the formatter see:
|
||||
http://docs.python.org/library/logging.html#formatter
|
||||
|
||||
"""
|
||||
|
||||
def format(self, record):
|
||||
"""Uses contextstring if request_id is set, otherwise default"""
|
||||
"""Uses contextstring if request_id is set, otherwise default."""
|
||||
if record.__dict__.get('request_id', None):
|
||||
self._fmt = FLAGS.logging_context_format_string
|
||||
else:
|
||||
@@ -214,20 +212,21 @@ class NovaFormatter(logging.Formatter):
|
||||
return logging.Formatter.format(self, record)
|
||||
|
||||
def formatException(self, exc_info, record=None):
|
||||
"""Format exception output with FLAGS.logging_exception_prefix"""
|
||||
"""Format exception output with FLAGS.logging_exception_prefix."""
|
||||
if not record:
|
||||
return logging.Formatter.formatException(self, exc_info)
|
||||
stringbuffer = cStringIO.StringIO()
|
||||
traceback.print_exception(exc_info[0], exc_info[1], exc_info[2],
|
||||
None, stringbuffer)
|
||||
lines = stringbuffer.getvalue().split("\n")
|
||||
lines = stringbuffer.getvalue().split('\n')
|
||||
stringbuffer.close()
|
||||
formatted_lines = []
|
||||
for line in lines:
|
||||
pl = FLAGS.logging_exception_prefix % record.__dict__
|
||||
fl = "%s%s" % (pl, line)
|
||||
fl = '%s%s' % (pl, line)
|
||||
formatted_lines.append(fl)
|
||||
return "\n".join(formatted_lines)
|
||||
return '\n'.join(formatted_lines)
|
||||
|
||||
|
||||
_formatter = NovaFormatter()
|
||||
|
||||
@@ -241,7 +240,7 @@ class NovaRootLogger(NovaLogger):
|
||||
NovaLogger.__init__(self, name, level)
|
||||
|
||||
def setup_from_flags(self):
|
||||
"""Setup logger from flags"""
|
||||
"""Setup logger from flags."""
|
||||
global _filelog
|
||||
if FLAGS.use_syslog:
|
||||
self.syslog = SysLogHandler(address='/dev/log')
|
||||
|
152
nova/rpc.py
152
nova/rpc.py
@@ -16,9 +16,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
AMQP-based RPC. Queues have consumers and publishers.
|
||||
"""AMQP-based RPC.
|
||||
|
||||
Queues have consumers and publishers.
|
||||
|
||||
No fan-out support yet.
|
||||
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -40,17 +43,19 @@ from nova import log as logging
|
||||
from nova import utils
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger('nova.rpc')
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_integer('rpc_thread_pool_size', 1024, 'Size of RPC thread pool')
|
||||
|
||||
|
||||
class Connection(carrot_connection.BrokerConnection):
|
||||
"""Connection instance object"""
|
||||
"""Connection instance object."""
|
||||
|
||||
@classmethod
|
||||
def instance(cls, new=True):
|
||||
"""Returns the instance"""
|
||||
"""Returns the instance."""
|
||||
if new or not hasattr(cls, '_instance'):
|
||||
params = dict(hostname=FLAGS.rabbit_host,
|
||||
port=FLAGS.rabbit_port,
|
||||
@@ -71,9 +76,11 @@ class Connection(carrot_connection.BrokerConnection):
|
||||
|
||||
@classmethod
|
||||
def recreate(cls):
|
||||
"""Recreates the connection instance
|
||||
"""Recreates the connection instance.
|
||||
|
||||
This is necessary to recover from some network errors/disconnects"""
|
||||
This is necessary to recover from some network errors/disconnects.
|
||||
|
||||
"""
|
||||
try:
|
||||
del cls._instance
|
||||
except AttributeError, e:
|
||||
@@ -84,10 +91,12 @@ class Connection(carrot_connection.BrokerConnection):
|
||||
|
||||
|
||||
class Consumer(messaging.Consumer):
|
||||
"""Consumer base class
|
||||
"""Consumer base class.
|
||||
|
||||
Contains methods for connecting the fetch method to async loops.
|
||||
|
||||
Contains methods for connecting the fetch method to async loops
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
for i in xrange(FLAGS.rabbit_max_retries):
|
||||
if i > 0:
|
||||
@@ -100,19 +109,18 @@ class Consumer(messaging.Consumer):
|
||||
fl_host = FLAGS.rabbit_host
|
||||
fl_port = FLAGS.rabbit_port
|
||||
fl_intv = FLAGS.rabbit_retry_interval
|
||||
LOG.error(_("AMQP server on %(fl_host)s:%(fl_port)d is"
|
||||
" unreachable: %(e)s. Trying again in %(fl_intv)d"
|
||||
" seconds.")
|
||||
% locals())
|
||||
LOG.error(_('AMQP server on %(fl_host)s:%(fl_port)d is'
|
||||
' unreachable: %(e)s. Trying again in %(fl_intv)d'
|
||||
' seconds.') % locals())
|
||||
self.failed_connection = True
|
||||
if self.failed_connection:
|
||||
LOG.error(_("Unable to connect to AMQP server "
|
||||
"after %d tries. Shutting down."),
|
||||
LOG.error(_('Unable to connect to AMQP server '
|
||||
'after %d tries. Shutting down.'),
|
||||
FLAGS.rabbit_max_retries)
|
||||
sys.exit(1)
|
||||
|
||||
def fetch(self, no_ack=None, auto_ack=None, enable_callbacks=False):
|
||||
"""Wraps the parent fetch with some logic for failed connections"""
|
||||
"""Wraps the parent fetch with some logic for failed connection."""
|
||||
# TODO(vish): the logic for failed connections and logging should be
|
||||
# refactored into some sort of connection manager object
|
||||
try:
|
||||
@@ -125,14 +133,14 @@ class Consumer(messaging.Consumer):
|
||||
self.declare()
|
||||
super(Consumer, self).fetch(no_ack, auto_ack, enable_callbacks)
|
||||
if self.failed_connection:
|
||||
LOG.error(_("Reconnected to queue"))
|
||||
LOG.error(_('Reconnected to queue'))
|
||||
self.failed_connection = False
|
||||
# NOTE(vish): This is catching all errors because we really don't
|
||||
# want exceptions to be logged 10 times a second if some
|
||||
# persistent failure occurs.
|
||||
except Exception, e: # pylint: disable=W0703
|
||||
if not self.failed_connection:
|
||||
LOG.exception(_("Failed to fetch message from queue: %s" % e))
|
||||
LOG.exception(_('Failed to fetch message from queue: %s' % e))
|
||||
self.failed_connection = True
|
||||
|
||||
def attach_to_eventlet(self):
|
||||
@@ -143,8 +151,9 @@ class Consumer(messaging.Consumer):
|
||||
|
||||
|
||||
class AdapterConsumer(Consumer):
|
||||
"""Calls methods on a proxy object based on method and args"""
|
||||
def __init__(self, connection=None, topic="broadcast", proxy=None):
|
||||
"""Calls methods on a proxy object based on method and args."""
|
||||
|
||||
def __init__(self, connection=None, topic='broadcast', proxy=None):
|
||||
LOG.debug(_('Initing the Adapter Consumer for %s') % topic)
|
||||
self.proxy = proxy
|
||||
self.pool = greenpool.GreenPool(FLAGS.rpc_thread_pool_size)
|
||||
@@ -156,13 +165,14 @@ class AdapterConsumer(Consumer):
|
||||
|
||||
@exception.wrap_exception
|
||||
def _receive(self, message_data, message):
|
||||
"""Magically looks for a method on the proxy object and calls it
|
||||
"""Magically looks for a method on the proxy object and calls it.
|
||||
|
||||
Message data should be a dictionary with two keys:
|
||||
method: string representing the method to call
|
||||
args: dictionary of arg: value
|
||||
|
||||
Example: {'method': 'echo', 'args': {'value': 42}}
|
||||
|
||||
"""
|
||||
LOG.debug(_('received %s') % message_data)
|
||||
msg_id = message_data.pop('_msg_id', None)
|
||||
@@ -189,22 +199,23 @@ class AdapterConsumer(Consumer):
|
||||
if msg_id:
|
||||
msg_reply(msg_id, rval, None)
|
||||
except Exception as e:
|
||||
logging.exception("Exception during message handling")
|
||||
logging.exception('Exception during message handling')
|
||||
if msg_id:
|
||||
msg_reply(msg_id, None, sys.exc_info())
|
||||
return
|
||||
|
||||
|
||||
class Publisher(messaging.Publisher):
|
||||
"""Publisher base class"""
|
||||
"""Publisher base class."""
|
||||
pass
|
||||
|
||||
|
||||
class TopicAdapterConsumer(AdapterConsumer):
|
||||
"""Consumes messages on a specific topic"""
|
||||
exchange_type = "topic"
|
||||
"""Consumes messages on a specific topic."""
|
||||
|
||||
def __init__(self, connection=None, topic="broadcast", proxy=None):
|
||||
exchange_type = 'topic'
|
||||
|
||||
def __init__(self, connection=None, topic='broadcast', proxy=None):
|
||||
self.queue = topic
|
||||
self.routing_key = topic
|
||||
self.exchange = FLAGS.control_exchange
|
||||
@@ -214,27 +225,29 @@ class TopicAdapterConsumer(AdapterConsumer):
|
||||
|
||||
|
||||
class FanoutAdapterConsumer(AdapterConsumer):
|
||||
"""Consumes messages from a fanout exchange"""
|
||||
exchange_type = "fanout"
|
||||
"""Consumes messages from a fanout exchange."""
|
||||
|
||||
def __init__(self, connection=None, topic="broadcast", proxy=None):
|
||||
self.exchange = "%s_fanout" % topic
|
||||
exchange_type = 'fanout'
|
||||
|
||||
def __init__(self, connection=None, topic='broadcast', proxy=None):
|
||||
self.exchange = '%s_fanout' % topic
|
||||
self.routing_key = topic
|
||||
unique = uuid.uuid4().hex
|
||||
self.queue = "%s_fanout_%s" % (topic, unique)
|
||||
self.queue = '%s_fanout_%s' % (topic, unique)
|
||||
self.durable = False
|
||||
LOG.info(_("Created '%(exchange)s' fanout exchange "
|
||||
"with '%(key)s' routing key"),
|
||||
dict(exchange=self.exchange, key=self.routing_key))
|
||||
LOG.info(_('Created "%(exchange)s" fanout exchange '
|
||||
'with "%(key)s" routing key'),
|
||||
dict(exchange=self.exchange, key=self.routing_key))
|
||||
super(FanoutAdapterConsumer, self).__init__(connection=connection,
|
||||
topic=topic, proxy=proxy)
|
||||
|
||||
|
||||
class TopicPublisher(Publisher):
|
||||
"""Publishes messages on a specific topic"""
|
||||
exchange_type = "topic"
|
||||
"""Publishes messages on a specific topic."""
|
||||
|
||||
def __init__(self, connection=None, topic="broadcast"):
|
||||
exchange_type = 'topic'
|
||||
|
||||
def __init__(self, connection=None, topic='broadcast'):
|
||||
self.routing_key = topic
|
||||
self.exchange = FLAGS.control_exchange
|
||||
self.durable = False
|
||||
@@ -243,20 +256,22 @@ class TopicPublisher(Publisher):
|
||||
|
||||
class FanoutPublisher(Publisher):
|
||||
"""Publishes messages to a fanout exchange."""
|
||||
exchange_type = "fanout"
|
||||
|
||||
exchange_type = 'fanout'
|
||||
|
||||
def __init__(self, topic, connection=None):
|
||||
self.exchange = "%s_fanout" % topic
|
||||
self.queue = "%s_fanout" % topic
|
||||
self.exchange = '%s_fanout' % topic
|
||||
self.queue = '%s_fanout' % topic
|
||||
self.durable = False
|
||||
LOG.info(_("Creating '%(exchange)s' fanout exchange"),
|
||||
dict(exchange=self.exchange))
|
||||
LOG.info(_('Creating "%(exchange)s" fanout exchange'),
|
||||
dict(exchange=self.exchange))
|
||||
super(FanoutPublisher, self).__init__(connection=connection)
|
||||
|
||||
|
||||
class DirectConsumer(Consumer):
|
||||
"""Consumes messages directly on a channel specified by msg_id"""
|
||||
exchange_type = "direct"
|
||||
"""Consumes messages directly on a channel specified by msg_id."""
|
||||
|
||||
exchange_type = 'direct'
|
||||
|
||||
def __init__(self, connection=None, msg_id=None):
|
||||
self.queue = msg_id
|
||||
@@ -268,8 +283,9 @@ class DirectConsumer(Consumer):
|
||||
|
||||
|
||||
class DirectPublisher(Publisher):
|
||||
"""Publishes messages directly on a channel specified by msg_id"""
|
||||
exchange_type = "direct"
|
||||
"""Publishes messages directly on a channel specified by msg_id."""
|
||||
|
||||
exchange_type = 'direct'
|
||||
|
||||
def __init__(self, connection=None, msg_id=None):
|
||||
self.routing_key = msg_id
|
||||
@@ -279,9 +295,9 @@ class DirectPublisher(Publisher):
|
||||
|
||||
|
||||
def msg_reply(msg_id, reply=None, failure=None):
|
||||
"""Sends a reply or an error on the channel signified by msg_id
|
||||
"""Sends a reply or an error on the channel signified by msg_id.
|
||||
|
||||
failure should be a sys.exc_info() tuple.
|
||||
Failure should be a sys.exc_info() tuple.
|
||||
|
||||
"""
|
||||
if failure:
|
||||
@@ -303,17 +319,20 @@ def msg_reply(msg_id, reply=None, failure=None):
|
||||
|
||||
|
||||
class RemoteError(exception.Error):
|
||||
"""Signifies that a remote class has raised an exception
|
||||
"""Signifies that a remote class has raised an exception.
|
||||
|
||||
Containes a string representation of the type of the original exception,
|
||||
the value of the original exception, and the traceback. These are
|
||||
sent to the parent as a joined string so printing the exception
|
||||
contains all of the relevent info."""
|
||||
contains all of the relevent info.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, exc_type, value, traceback):
|
||||
self.exc_type = exc_type
|
||||
self.value = value
|
||||
self.traceback = traceback
|
||||
super(RemoteError, self).__init__("%s %s\n%s" % (exc_type,
|
||||
super(RemoteError, self).__init__('%s %s\n%s' % (exc_type,
|
||||
value,
|
||||
traceback))
|
||||
|
||||
@@ -339,6 +358,7 @@ def _pack_context(msg, context):
|
||||
context out into a bunch of separate keys. If we want to support
|
||||
more arguments in rabbit messages, we may want to do the same
|
||||
for args at some point.
|
||||
|
||||
"""
|
||||
context = dict([('_context_%s' % key, value)
|
||||
for (key, value) in context.to_dict().iteritems()])
|
||||
@@ -346,11 +366,11 @@ def _pack_context(msg, context):
|
||||
|
||||
|
||||
def call(context, topic, msg):
|
||||
"""Sends a message on a topic and wait for a response"""
|
||||
LOG.debug(_("Making asynchronous call on %s ..."), topic)
|
||||
"""Sends a message on a topic and wait for a response."""
|
||||
LOG.debug(_('Making asynchronous call on %s ...'), topic)
|
||||
msg_id = uuid.uuid4().hex
|
||||
msg.update({'_msg_id': msg_id})
|
||||
LOG.debug(_("MSG_ID is %s") % (msg_id))
|
||||
LOG.debug(_('MSG_ID is %s') % (msg_id))
|
||||
_pack_context(msg, context)
|
||||
|
||||
class WaitMessage(object):
|
||||
@@ -387,8 +407,8 @@ def call(context, topic, msg):
|
||||
|
||||
|
||||
def cast(context, topic, msg):
|
||||
"""Sends a message on a topic without waiting for a response"""
|
||||
LOG.debug(_("Making asynchronous cast on %s..."), topic)
|
||||
"""Sends a message on a topic without waiting for a response."""
|
||||
LOG.debug(_('Making asynchronous cast on %s...'), topic)
|
||||
_pack_context(msg, context)
|
||||
conn = Connection.instance()
|
||||
publisher = TopicPublisher(connection=conn, topic=topic)
|
||||
@@ -397,8 +417,8 @@ def cast(context, topic, msg):
|
||||
|
||||
|
||||
def fanout_cast(context, topic, msg):
|
||||
"""Sends a message on a fanout exchange without waiting for a response"""
|
||||
LOG.debug(_("Making asynchronous fanout cast..."))
|
||||
"""Sends a message on a fanout exchange without waiting for a response."""
|
||||
LOG.debug(_('Making asynchronous fanout cast...'))
|
||||
_pack_context(msg, context)
|
||||
conn = Connection.instance()
|
||||
publisher = FanoutPublisher(topic, connection=conn)
|
||||
@@ -407,14 +427,14 @@ def fanout_cast(context, topic, msg):
|
||||
|
||||
|
||||
def generic_response(message_data, message):
|
||||
"""Logs a result and exits"""
|
||||
"""Logs a result and exits."""
|
||||
LOG.debug(_('response %s'), message_data)
|
||||
message.ack()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def send_message(topic, message, wait=True):
|
||||
"""Sends a message for testing"""
|
||||
"""Sends a message for testing."""
|
||||
msg_id = uuid.uuid4().hex
|
||||
message.update({'_msg_id': msg_id})
|
||||
LOG.debug(_('topic is %s'), topic)
|
||||
@@ -425,14 +445,14 @@ def send_message(topic, message, wait=True):
|
||||
queue=msg_id,
|
||||
exchange=msg_id,
|
||||
auto_delete=True,
|
||||
exchange_type="direct",
|
||||
exchange_type='direct',
|
||||
routing_key=msg_id)
|
||||
consumer.register_callback(generic_response)
|
||||
|
||||
publisher = messaging.Publisher(connection=Connection.instance(),
|
||||
exchange=FLAGS.control_exchange,
|
||||
durable=False,
|
||||
exchange_type="topic",
|
||||
exchange_type='topic',
|
||||
routing_key=topic)
|
||||
publisher.send(message)
|
||||
publisher.close()
|
||||
@@ -441,8 +461,8 @@ def send_message(topic, message, wait=True):
|
||||
consumer.wait()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# NOTE(vish): you can send messages from the command line using
|
||||
# topic and a json sting representing a dictionary
|
||||
# for the method
|
||||
if __name__ == '__main__':
|
||||
# You can send messages from the command line using
|
||||
# topic and a json string representing a dictionary
|
||||
# for the method
|
||||
send_message(sys.argv[1], json.loads(sys.argv[2]))
|
||||
|
@@ -75,16 +75,25 @@ class InstanceTypeTestCase(test.TestCase):
|
||||
def test_invalid_create_args_should_fail(self):
|
||||
"""Ensures that instance type creation fails with invalid args"""
|
||||
self.assertRaises(
|
||||
exception.InvalidInputException,
|
||||
exception.InvalidInput,
|
||||
instance_types.create, self.name, 0, 1, 120, self.flavorid)
|
||||
self.assertRaises(
|
||||
exception.InvalidInputException,
|
||||
exception.InvalidInput,
|
||||
instance_types.create, self.name, 256, -1, 120, self.flavorid)
|
||||
self.assertRaises(
|
||||
exception.InvalidInputException,
|
||||
exception.InvalidInput,
|
||||
instance_types.create, self.name, 256, 1, "aa", self.flavorid)
|
||||
|
||||
def test_non_existant_inst_type_shouldnt_delete(self):
|
||||
"""Ensures that instance type creation fails with invalid args"""
|
||||
self.assertRaises(exception.ApiError,
|
||||
instance_types.destroy, "sfsfsdfdfs")
|
||||
|
||||
def test_repeated_inst_types_should_raise_api_error(self):
|
||||
"""Ensures that instance duplicates raises ApiError"""
|
||||
new_name = self.name + "dup"
|
||||
instance_types.create(new_name, 256, 1, 120, self.flavorid + 1)
|
||||
instance_types.destroy(new_name)
|
||||
self.assertRaises(
|
||||
exception.ApiError,
|
||||
instance_types.create, new_name, 256, 1, 120, self.flavorid)
|
||||
|
@@ -120,12 +120,11 @@ class SchedulerTestCase(test.TestCase):
|
||||
dest = 'dummydest'
|
||||
ctxt = context.get_admin_context()
|
||||
|
||||
try:
|
||||
scheduler.show_host_resources(ctxt, dest)
|
||||
except exception.NotFound, e:
|
||||
c1 = (e.message.find(_("does not exist or is not a "
|
||||
"compute node.")) >= 0)
|
||||
self.assertTrue(c1)
|
||||
self.assertRaises(exception.NotFound, scheduler.show_host_resources,
|
||||
ctxt, dest)
|
||||
#TODO(bcwaldon): reimplement this functionality
|
||||
#c1 = (e.message.find(_("does not exist or is not a "
|
||||
# "compute node.")) >= 0)
|
||||
|
||||
def _dic_is_equal(self, dic1, dic2, keys=None):
|
||||
"""Compares 2 dictionary contents(Helper method)"""
|
||||
@@ -698,14 +697,10 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
'topic': 'volume', 'report_count': 0}
|
||||
s_ref = db.service_create(self.context, dic)
|
||||
|
||||
try:
|
||||
self.scheduler.driver.schedule_live_migration(self.context,
|
||||
instance_id,
|
||||
i_ref['host'])
|
||||
except exception.Invalid, e:
|
||||
c = (e.message.find('volume node is not alive') >= 0)
|
||||
self.assertRaises(exception.VolumeServiceUnavailable,
|
||||
self.scheduler.driver.schedule_live_migration,
|
||||
self.context, instance_id, i_ref['host'])
|
||||
|
||||
self.assertTrue(c)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
db.volume_destroy(self.context, v_ref['id'])
|
||||
@@ -718,13 +713,10 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
s_ref = self._create_compute_service(created_at=t, updated_at=t,
|
||||
host=i_ref['host'])
|
||||
|
||||
try:
|
||||
self.scheduler.driver._live_migration_src_check(self.context,
|
||||
i_ref)
|
||||
except exception.Invalid, e:
|
||||
c = (e.message.find('is not alive') >= 0)
|
||||
self.assertRaises(exception.ComputeServiceUnavailable,
|
||||
self.scheduler.driver._live_migration_src_check,
|
||||
self.context, i_ref)
|
||||
|
||||
self.assertTrue(c)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
|
||||
@@ -737,7 +729,7 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
ret = self.scheduler.driver._live_migration_src_check(self.context,
|
||||
i_ref)
|
||||
|
||||
self.assertTrue(ret == None)
|
||||
self.assertTrue(ret is None)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
|
||||
@@ -749,14 +741,10 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
s_ref = self._create_compute_service(created_at=t, updated_at=t,
|
||||
host=i_ref['host'])
|
||||
|
||||
try:
|
||||
self.scheduler.driver._live_migration_dest_check(self.context,
|
||||
i_ref,
|
||||
i_ref['host'])
|
||||
except exception.Invalid, e:
|
||||
c = (e.message.find('is not alive') >= 0)
|
||||
self.assertRaises(exception.ComputeServiceUnavailable,
|
||||
self.scheduler.driver._live_migration_dest_check,
|
||||
self.context, i_ref, i_ref['host'])
|
||||
|
||||
self.assertTrue(c)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
|
||||
@@ -766,14 +754,10 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
i_ref = db.instance_get(self.context, instance_id)
|
||||
s_ref = self._create_compute_service(host=i_ref['host'])
|
||||
|
||||
try:
|
||||
self.scheduler.driver._live_migration_dest_check(self.context,
|
||||
i_ref,
|
||||
i_ref['host'])
|
||||
except exception.Invalid, e:
|
||||
c = (e.message.find('choose other host') >= 0)
|
||||
self.assertRaises(exception.UnableToMigrateToSelf,
|
||||
self.scheduler.driver._live_migration_dest_check,
|
||||
self.context, i_ref, i_ref['host'])
|
||||
|
||||
self.assertTrue(c)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
|
||||
@@ -784,14 +768,10 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
s_ref = self._create_compute_service(host='somewhere',
|
||||
memory_mb_used=12)
|
||||
|
||||
try:
|
||||
self.scheduler.driver._live_migration_dest_check(self.context,
|
||||
i_ref,
|
||||
'somewhere')
|
||||
except exception.NotEmpty, e:
|
||||
c = (e.message.find('Unable to migrate') >= 0)
|
||||
self.assertRaises(exception.MigrationError,
|
||||
self.scheduler.driver._live_migration_dest_check,
|
||||
self.context, i_ref, 'somewhere')
|
||||
|
||||
self.assertTrue(c)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
|
||||
@@ -805,7 +785,7 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
ret = self.scheduler.driver._live_migration_dest_check(self.context,
|
||||
i_ref,
|
||||
'somewhere')
|
||||
self.assertTrue(ret == None)
|
||||
self.assertTrue(ret is None)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
|
||||
@@ -837,14 +817,10 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
"args": {'filename': fpath}})
|
||||
|
||||
self.mox.ReplayAll()
|
||||
try:
|
||||
self.scheduler.driver._live_migration_common_check(self.context,
|
||||
i_ref,
|
||||
dest)
|
||||
except exception.Invalid, e:
|
||||
c = (e.message.find('does not exist') >= 0)
|
||||
self.assertRaises(exception.SourceHostUnavailable,
|
||||
self.scheduler.driver._live_migration_common_check,
|
||||
self.context, i_ref, dest)
|
||||
|
||||
self.assertTrue(c)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
|
||||
@@ -865,14 +841,10 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
driver.mounted_on_same_shared_storage(mox.IgnoreArg(), i_ref, dest)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
try:
|
||||
self.scheduler.driver._live_migration_common_check(self.context,
|
||||
i_ref,
|
||||
dest)
|
||||
except exception.Invalid, e:
|
||||
c = (e.message.find(_('Different hypervisor type')) >= 0)
|
||||
self.assertRaises(exception.InvalidHypervisorType,
|
||||
self.scheduler.driver._live_migration_common_check,
|
||||
self.context, i_ref, dest)
|
||||
|
||||
self.assertTrue(c)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
db.service_destroy(self.context, s_ref2['id'])
|
||||
@@ -895,14 +867,10 @@ class SimpleDriverTestCase(test.TestCase):
|
||||
driver.mounted_on_same_shared_storage(mox.IgnoreArg(), i_ref, dest)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
try:
|
||||
self.scheduler.driver._live_migration_common_check(self.context,
|
||||
i_ref,
|
||||
dest)
|
||||
except exception.Invalid, e:
|
||||
c = (e.message.find(_('Older hypervisor version')) >= 0)
|
||||
self.assertRaises(exception.DestinationHypervisorTooOld,
|
||||
self.scheduler.driver._live_migration_common_check,
|
||||
self.context, i_ref, dest)
|
||||
|
||||
self.assertTrue(c)
|
||||
db.instance_destroy(self.context, instance_id)
|
||||
db.service_destroy(self.context, s_ref['id'])
|
||||
db.service_destroy(self.context, s_ref2['id'])
|
||||
@@ -968,7 +936,7 @@ class FakeRerouteCompute(api.reroute_compute):
|
||||
|
||||
|
||||
def go_boom(self, context, instance):
|
||||
raise exception.InstanceNotFound("boom message", instance)
|
||||
raise exception.InstanceNotFound(instance_id=instance)
|
||||
|
||||
|
||||
def found_instance(self, context, instance):
|
||||
@@ -1017,11 +985,8 @@ class ZoneRedirectTest(test.TestCase):
|
||||
def test_routing_flags(self):
|
||||
FLAGS.enable_zone_routing = False
|
||||
decorator = FakeRerouteCompute("foo")
|
||||
try:
|
||||
result = decorator(go_boom)(None, None, 1)
|
||||
self.assertFail(_("Should have thrown exception."))
|
||||
except exception.InstanceNotFound, e:
|
||||
self.assertEquals(e.message, 'boom message')
|
||||
self.assertRaises(exception.InstanceNotFound, decorator(go_boom),
|
||||
None, None, 1)
|
||||
|
||||
def test_get_collection_context_and_id(self):
|
||||
decorator = api.reroute_compute("foo")
|
||||
|
@@ -31,9 +31,7 @@ from nova import test
|
||||
from nova import utils
|
||||
from nova.api.ec2 import cloud
|
||||
from nova.auth import manager
|
||||
from nova.compute import manager as compute_manager
|
||||
from nova.compute import power_state
|
||||
from nova.db.sqlalchemy import models
|
||||
from nova.virt import libvirt_conn
|
||||
|
||||
libvirt = None
|
||||
@@ -46,6 +44,22 @@ def _concurrency(wait, done, target):
|
||||
done.send()
|
||||
|
||||
|
||||
def _create_network_info(count=1):
|
||||
fake = 'fake'
|
||||
fake_ip = '0.0.0.0/0'
|
||||
fake_ip_2 = '0.0.0.1/0'
|
||||
fake_ip_3 = '0.0.0.1/0'
|
||||
network = {'gateway': fake,
|
||||
'gateway_v6': fake,
|
||||
'bridge': fake,
|
||||
'cidr': fake_ip,
|
||||
'cidr_v6': fake_ip}
|
||||
mapping = {'mac': fake,
|
||||
'ips': [{'ip': fake_ip}, {'ip': fake_ip}],
|
||||
'ip6s': [{'ip': fake_ip}, {'ip': fake_ip_2}, {'ip': fake_ip_3}]}
|
||||
return [(network, mapping) for x in xrange(0, count)]
|
||||
|
||||
|
||||
class CacheConcurrencyTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(CacheConcurrencyTestCase, self).setUp()
|
||||
@@ -194,6 +208,37 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
|
||||
return db.service_create(context.get_admin_context(), service_ref)
|
||||
|
||||
def test_preparing_xml_info(self):
|
||||
conn = libvirt_conn.LibvirtConnection(True)
|
||||
instance_ref = db.instance_create(self.context, self.test_instance)
|
||||
|
||||
result = conn._prepare_xml_info(instance_ref, False)
|
||||
self.assertFalse(result['nics'])
|
||||
|
||||
result = conn._prepare_xml_info(instance_ref, False,
|
||||
_create_network_info())
|
||||
self.assertTrue(len(result['nics']) == 1)
|
||||
|
||||
result = conn._prepare_xml_info(instance_ref, False,
|
||||
_create_network_info(2))
|
||||
self.assertTrue(len(result['nics']) == 2)
|
||||
|
||||
def test_get_nic_for_xml_v4(self):
|
||||
conn = libvirt_conn.LibvirtConnection(True)
|
||||
network, mapping = _create_network_info()[0]
|
||||
self.flags(use_ipv6=False)
|
||||
params = conn._get_nic_for_xml(network, mapping)['extra_params']
|
||||
self.assertTrue(params.find('PROJNETV6') == -1)
|
||||
self.assertTrue(params.find('PROJMASKV6') == -1)
|
||||
|
||||
def test_get_nic_for_xml_v6(self):
|
||||
conn = libvirt_conn.LibvirtConnection(True)
|
||||
network, mapping = _create_network_info()[0]
|
||||
self.flags(use_ipv6=True)
|
||||
params = conn._get_nic_for_xml(network, mapping)['extra_params']
|
||||
self.assertTrue(params.find('PROJNETV6') > -1)
|
||||
self.assertTrue(params.find('PROJMASKV6') > -1)
|
||||
|
||||
def test_xml_and_uri_no_ramdisk_no_kernel(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
self._check_xml_and_uri(instance_data,
|
||||
@@ -229,6 +274,22 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
instance_data = dict(self.test_instance)
|
||||
self._check_xml_and_container(instance_data)
|
||||
|
||||
def test_multi_nic(self):
|
||||
instance_data = dict(self.test_instance)
|
||||
network_info = _create_network_info(2)
|
||||
conn = libvirt_conn.LibvirtConnection(True)
|
||||
instance_ref = db.instance_create(self.context, instance_data)
|
||||
xml = conn.to_xml(instance_ref, False, network_info)
|
||||
tree = xml_to_tree(xml)
|
||||
interfaces = tree.findall("./devices/interface")
|
||||
self.assertEquals(len(interfaces), 2)
|
||||
parameters = interfaces[0].findall('./filterref/parameter')
|
||||
self.assertEquals(interfaces[0].get('type'), 'bridge')
|
||||
self.assertEquals(parameters[0].get('name'), 'IP')
|
||||
self.assertEquals(parameters[0].get('value'), '0.0.0.0/0')
|
||||
self.assertEquals(parameters[1].get('name'), 'DHCPSERVER')
|
||||
self.assertEquals(parameters[1].get('value'), 'fake')
|
||||
|
||||
def _check_xml_and_container(self, instance):
|
||||
user_context = context.RequestContext(project=self.project,
|
||||
user=self.user)
|
||||
@@ -327,19 +388,13 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
check = (lambda t: t.find('./os/initrd'), None)
|
||||
check_list.append(check)
|
||||
|
||||
parameter = './devices/interface/filterref/parameter'
|
||||
common_checks = [
|
||||
(lambda t: t.find('.').tag, 'domain'),
|
||||
(lambda t: t.find(
|
||||
'./devices/interface/filterref/parameter').get('name'), 'IP'),
|
||||
(lambda t: t.find(
|
||||
'./devices/interface/filterref/parameter').get(
|
||||
'value'), '10.11.12.13'),
|
||||
(lambda t: t.findall(
|
||||
'./devices/interface/filterref/parameter')[1].get(
|
||||
'name'), 'DHCPSERVER'),
|
||||
(lambda t: t.findall(
|
||||
'./devices/interface/filterref/parameter')[1].get(
|
||||
'value'), '10.0.0.1'),
|
||||
(lambda t: t.find(parameter).get('name'), 'IP'),
|
||||
(lambda t: t.find(parameter).get('value'), '10.11.12.13'),
|
||||
(lambda t: t.findall(parameter)[1].get('name'), 'DHCPSERVER'),
|
||||
(lambda t: t.findall(parameter)[1].get('value'), '10.0.0.1'),
|
||||
(lambda t: t.find('./devices/serial/source').get(
|
||||
'path').split('/')[1], 'console.log'),
|
||||
(lambda t: t.find('./memory').text, '2097152')]
|
||||
@@ -451,7 +506,7 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
|
||||
self.mox.ReplayAll()
|
||||
conn = libvirt_conn.LibvirtConnection(False)
|
||||
self.assertRaises(exception.Invalid,
|
||||
self.assertRaises(exception.ComputeServiceUnavailable,
|
||||
conn.update_available_resource,
|
||||
self.context, 'dummy')
|
||||
|
||||
@@ -549,6 +604,43 @@ class LibvirtConnTestCase(test.TestCase):
|
||||
db.volume_destroy(self.context, volume_ref['id'])
|
||||
db.instance_destroy(self.context, instance_ref['id'])
|
||||
|
||||
def test_spawn_with_network_info(self):
|
||||
# Skip if non-libvirt environment
|
||||
if not self.lazy_load_library_exists():
|
||||
return
|
||||
|
||||
# Preparing mocks
|
||||
def fake_none(self, instance):
|
||||
return
|
||||
|
||||
self.create_fake_libvirt_mock()
|
||||
instance = db.instance_create(self.context, self.test_instance)
|
||||
|
||||
# Start test
|
||||
self.mox.ReplayAll()
|
||||
conn = libvirt_conn.LibvirtConnection(False)
|
||||
conn.firewall_driver.setattr('setup_basic_filtering', fake_none)
|
||||
conn.firewall_driver.setattr('prepare_instance_filter', fake_none)
|
||||
|
||||
network = db.project_get_network(context.get_admin_context(),
|
||||
self.project.id)
|
||||
ip_dict = {'ip': self.test_ip,
|
||||
'netmask': network['netmask'],
|
||||
'enabled': '1'}
|
||||
mapping = {'label': network['label'],
|
||||
'gateway': network['gateway'],
|
||||
'mac': instance['mac_address'],
|
||||
'dns': [network['dns']],
|
||||
'ips': [ip_dict]}
|
||||
network_info = [(network, mapping)]
|
||||
|
||||
try:
|
||||
conn.spawn(instance, network_info)
|
||||
except Exception, e:
|
||||
count = (0 <= e.message.find('Unexpected method call'))
|
||||
|
||||
self.assertTrue(count)
|
||||
|
||||
def tearDown(self):
|
||||
self.manager.delete_project(self.project)
|
||||
self.manager.delete_user(self.user)
|
||||
@@ -614,11 +706,15 @@ class IptablesFirewallTestCase(test.TestCase):
|
||||
'# Completed on Tue Jan 18 23:47:56 2011',
|
||||
]
|
||||
|
||||
def _create_instance_ref(self):
|
||||
return db.instance_create(self.context,
|
||||
{'user_id': 'fake',
|
||||
'project_id': 'fake',
|
||||
'mac_address': '56:12:12:12:12:12',
|
||||
'instance_type_id': 1})
|
||||
|
||||
def test_static_filters(self):
|
||||
instance_ref = db.instance_create(self.context,
|
||||
{'user_id': 'fake',
|
||||
'project_id': 'fake',
|
||||
'mac_address': '56:12:12:12:12:12'})
|
||||
instance_ref = self._create_instance_ref()
|
||||
ip = '10.11.12.13'
|
||||
|
||||
network_ref = db.project_get_network(self.context,
|
||||
@@ -729,6 +825,32 @@ class IptablesFirewallTestCase(test.TestCase):
|
||||
"TCP port 80/81 acceptance rule wasn't added")
|
||||
db.instance_destroy(admin_ctxt, instance_ref['id'])
|
||||
|
||||
def test_filters_for_instance(self):
|
||||
network_info = _create_network_info()
|
||||
rulesv4, rulesv6 = self.fw._filters_for_instance("fake", network_info)
|
||||
self.assertEquals(len(rulesv4), 2)
|
||||
self.assertEquals(len(rulesv6), 3)
|
||||
|
||||
def multinic_iptables_test(self):
|
||||
ipv4_rules_per_network = 2
|
||||
ipv6_rules_per_network = 3
|
||||
networks_count = 5
|
||||
instance_ref = self._create_instance_ref()
|
||||
network_info = _create_network_info(networks_count)
|
||||
ipv4_len = len(self.fw.iptables.ipv4['filter'].rules)
|
||||
ipv6_len = len(self.fw.iptables.ipv6['filter'].rules)
|
||||
inst_ipv4, inst_ipv6 = self.fw.instance_rules(instance_ref,
|
||||
network_info)
|
||||
self.fw.add_filters_for_instance(instance_ref, network_info)
|
||||
ipv4 = self.fw.iptables.ipv4['filter'].rules
|
||||
ipv6 = self.fw.iptables.ipv6['filter'].rules
|
||||
ipv4_network_rules = len(ipv4) - len(inst_ipv4) - ipv4_len
|
||||
ipv6_network_rules = len(ipv6) - len(inst_ipv6) - ipv6_len
|
||||
self.assertEquals(ipv4_network_rules,
|
||||
ipv4_rules_per_network * networks_count)
|
||||
self.assertEquals(ipv6_network_rules,
|
||||
ipv6_rules_per_network * networks_count)
|
||||
|
||||
|
||||
class NWFilterTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
@@ -810,6 +932,28 @@ class NWFilterTestCase(test.TestCase):
|
||||
|
||||
return db.security_group_get_by_name(self.context, 'fake', 'testgroup')
|
||||
|
||||
def _create_instance(self):
|
||||
return db.instance_create(self.context,
|
||||
{'user_id': 'fake',
|
||||
'project_id': 'fake',
|
||||
'mac_address': '00:A0:C9:14:C8:29',
|
||||
'instance_type_id': 1})
|
||||
|
||||
def _create_instance_type(self, params={}):
|
||||
"""Create a test instance"""
|
||||
context = self.context.elevated()
|
||||
inst = {}
|
||||
inst['name'] = 'm1.small'
|
||||
inst['memory_mb'] = '1024'
|
||||
inst['vcpus'] = '1'
|
||||
inst['local_gb'] = '20'
|
||||
inst['flavorid'] = '1'
|
||||
inst['swap'] = '2048'
|
||||
inst['rxtx_quota'] = 100
|
||||
inst['rxtx_cap'] = 200
|
||||
inst.update(params)
|
||||
return db.instance_type_create(context, inst)['id']
|
||||
|
||||
def test_creates_base_rule_first(self):
|
||||
# These come pre-defined by libvirt
|
||||
self.defined_filters = ['no-mac-spoofing',
|
||||
@@ -838,24 +982,18 @@ class NWFilterTestCase(test.TestCase):
|
||||
|
||||
self.fake_libvirt_connection.nwfilterDefineXML = _filterDefineXMLMock
|
||||
|
||||
instance_ref = db.instance_create(self.context,
|
||||
{'user_id': 'fake',
|
||||
'project_id': 'fake',
|
||||
'mac_address': '00:A0:C9:14:C8:29'})
|
||||
instance_ref = self._create_instance()
|
||||
inst_id = instance_ref['id']
|
||||
|
||||
ip = '10.11.12.13'
|
||||
|
||||
network_ref = db.project_get_network(self.context,
|
||||
'fake')
|
||||
|
||||
fixed_ip = {'address': ip,
|
||||
'network_id': network_ref['id']}
|
||||
network_ref = db.project_get_network(self.context, 'fake')
|
||||
fixed_ip = {'address': ip, 'network_id': network_ref['id']}
|
||||
|
||||
admin_ctxt = context.get_admin_context()
|
||||
db.fixed_ip_create(admin_ctxt, fixed_ip)
|
||||
db.fixed_ip_update(admin_ctxt, ip, {'allocated': True,
|
||||
'instance_id': instance_ref['id']})
|
||||
'instance_id': inst_id})
|
||||
|
||||
def _ensure_all_called():
|
||||
instance_filter = 'nova-instance-%s-%s' % (instance_ref['name'],
|
||||
@@ -881,3 +1019,11 @@ class NWFilterTestCase(test.TestCase):
|
||||
_ensure_all_called()
|
||||
self.teardown_security_group()
|
||||
db.instance_destroy(admin_ctxt, instance_ref['id'])
|
||||
|
||||
def test_create_network_filters(self):
|
||||
instance_ref = self._create_instance()
|
||||
network_info = _create_network_info(3)
|
||||
result = self.fw._create_network_filters(instance_ref,
|
||||
network_info,
|
||||
"fake")
|
||||
self.assertEquals(len(result), 3)
|
||||
|
@@ -142,7 +142,7 @@ class VolumeTestCase(test.TestCase):
|
||||
self.assertEqual(vol['status'], "available")
|
||||
|
||||
self.volume.delete_volume(self.context, volume_id)
|
||||
self.assertRaises(exception.Error,
|
||||
self.assertRaises(exception.VolumeNotFound,
|
||||
db.volume_get,
|
||||
self.context,
|
||||
volume_id)
|
||||
|
Reference in New Issue
Block a user