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