merge trunk

This commit is contained in:
Salvatore Orlando
2011-03-18 08:01:07 +00:00
11 changed files with 147 additions and 45 deletions

View File

@@ -40,6 +40,7 @@ Ken Pepple <ken.pepple@gmail.com>
Kevin L. Mitchell <kevin.mitchell@rackspace.com>
Koji Iida <iida.koji@lab.ntt.co.jp>
Lorin Hochstein <lorin@isi.edu>
Mark Washenberger <mark.washenberger@rackspace.com>
Masanori Itoh <itoumsn@nttdata.co.jp>
Matt Dietz <matt.dietz@rackspace.com>
Michael Gundlach <michael.gundlach@rackspace.com>

View File

@@ -446,10 +446,15 @@ class FixedIpCommands(object):
def list(self, host=None):
"""Lists all fixed ips (optionally by host) arguments: [host]"""
ctxt = context.get_admin_context()
try:
if host == None:
fixed_ips = db.fixed_ip_get_all(ctxt)
else:
fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)
except exception.NotFound as ex:
print "error: %s" % ex
sys.exit(2)
print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (_('network'),
_('IP address'),
@@ -574,8 +579,10 @@ class VmCommands(object):
ctxt = context.get_admin_context()
instance_id = ec2utils.ec2_id_to_id(ec2_id)
if FLAGS.connection_type != 'libvirt':
msg = _('Only KVM is supported for now. Sorry!')
if (FLAGS.connection_type != 'libvirt' or
(FLAGS.connection_type == 'libvirt' and
FLAGS.libvirt_type not in ['kvm', 'qemu'])):
msg = _('Only KVM and QEmu are supported for now. Sorry!')
raise exception.Error(msg)
if (FLAGS.volume_driver != 'nova.volume.driver.AOEDriver' and \
@@ -716,6 +723,49 @@ class DbCommands(object):
print migration.db_version()
class InstanceCommands(object):
"""Class for managing instances."""
def list(self, host=None, instance=None):
"""Show a list of all instances"""
print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
" %-10s %-10s %-10s %-5s" % (
_('instance'),
_('node'),
_('type'),
_('state'),
_('launched'),
_('image'),
_('kernel'),
_('ramdisk'),
_('project'),
_('user'),
_('zone'),
_('index'))
if host == None:
instances = db.instance_get_all(context.get_admin_context())
else:
instances = db.instance_get_all_by_host(
context.get_admin_context(), host)
for instance in instances:
print "%-10s %-15s %-10s %-10s %-19s %-12s %-12s %-12s" \
" %-10s %-10s %-10s %-5d" % (
instance['hostname'],
instance['host'],
instance['instance_type'],
instance['state_description'],
instance['launched_at'],
instance['image_id'],
instance['kernel_id'],
instance['ramdisk_id'],
instance['project_id'],
instance['user_id'],
instance['availability_zone'],
instance['launch_index'])
class VolumeCommands(object):
"""Methods for dealing with a cloud in an odd state"""
@@ -1002,7 +1052,8 @@ CATEGORIES = [
('volume', VolumeCommands),
('instance_type', InstanceTypeCommands),
('image', ImageCommands),
('flavor', InstanceTypeCommands)]
('flavor', InstanceTypeCommands),
('instance', InstanceCommands)]
def lazy_match(name, key_value_tuples):

View File

@@ -162,6 +162,8 @@ class DbDriver(object):
values['description'] = description
db.project_update(context.get_admin_context(), project_id, values)
if not self.is_in_project(manager_uid, project_id):
self.add_to_project(manager_uid, project_id)
def add_to_project(self, uid, project_id):
"""Add user to project"""

View File

@@ -275,6 +275,8 @@ class LdapDriver(object):
attr.append((self.ldap.MOD_REPLACE, 'description', description))
dn = self.__project_to_dn(project_id)
self.conn.modify_s(dn, attr)
if not self.is_in_project(manager_uid, project_id):
self.add_to_project(manager_uid, project_id)
@sanitize
def add_to_project(self, uid, project_id):

View File

@@ -46,7 +46,7 @@ class Error(Exception):
class ApiError(Error):
def __init__(self, message='Unknown', code='Unknown'):
def __init__(self, message='Unknown', code='ApiError'):
self.message = message
self.code = code
super(ApiError, self).__init__('%s: %s' % (code, message))

View File

@@ -37,6 +37,12 @@ flags.DEFINE_integer('quota_floating_ips', 10,
'number of floating ips allowed per project')
flags.DEFINE_integer('quota_metadata_items', 128,
'number of metadata items allowed per instance')
flags.DEFINE_integer('quota_max_injected_files', 5,
'number of injected files allowed')
flags.DEFINE_integer('quota_max_injected_file_content_bytes', 10 * 1024,
'number of bytes allowed per injected file')
flags.DEFINE_integer('quota_max_injected_file_path_bytes', 255,
'number of bytes allowed per injected file path')
def get_quota(context, project_id):
@@ -46,6 +52,7 @@ def get_quota(context, project_id):
'gigabytes': FLAGS.quota_gigabytes,
'floating_ips': FLAGS.quota_floating_ips,
'metadata_items': FLAGS.quota_metadata_items}
try:
quota = db.quota_get(context, project_id)
for key in rval.keys():
@@ -106,6 +113,21 @@ def allowed_metadata_items(context, num_metadata_items):
return min(num_metadata_items, num_allowed_metadata_items)
def allowed_injected_files(context):
"""Return the number of injected files allowed"""
return FLAGS.quota_max_injected_files
def allowed_injected_file_content_bytes(context):
"""Return the number of bytes allowed per injected file content"""
return FLAGS.quota_max_injected_file_content_bytes
def allowed_injected_file_path_bytes(context):
"""Return the number of bytes allowed in an injected file path"""
return FLAGS.quota_max_injected_file_path_bytes
class QuotaError(exception.ApiError):
"""Quota Exceeeded"""
pass

View File

@@ -311,7 +311,7 @@ def _pack_context(msg, context):
def call(context, topic, msg):
"""Sends a message on a topic and wait for a response"""
LOG.debug(_("Making asynchronous call..."))
LOG.debug(_("Making asynchronous call on %s ..."), topic)
msg_id = uuid.uuid4().hex
msg.update({'_msg_id': msg_id})
LOG.debug(_("MSG_ID is %s") % (msg_id))
@@ -352,7 +352,7 @@ def call(context, topic, msg):
def cast(context, topic, msg):
"""Sends a message on a topic without waiting for a response"""
LOG.debug(_("Making asynchronous cast..."))
LOG.debug(_("Making asynchronous cast on %s..."), topic)
_pack_context(msg, context)
conn = Connection.instance()
publisher = TopicPublisher(connection=conn, topic=topic)

View File

@@ -299,6 +299,13 @@ class AuthManagerTestCase(object):
self.assertEqual('test2', project.project_manager_id)
self.assertEqual('new desc', project.description)
def test_modify_project_adds_new_manager(self):
with user_and_project_generator(self.manager):
with user_generator(self.manager, name='test2'):
self.manager.modify_project('testproj', 'test2', 'new desc')
project = self.manager.get_project('testproj')
self.assertTrue('test2' in project.member_ids)
def test_can_delete_project(self):
with user_generator(self.manager):
self.manager.create_project('testproj', 'test1')

View File

@@ -336,8 +336,8 @@ class ISCSITestCase(DriverTestCase):
self.mox.StubOutWithMock(self.volume.driver, '_execute')
for i in volume_id_list:
tid = db.volume_get_iscsi_target_num(self.context, i)
self.volume.driver._execute("sudo ietadm --op show --tid=%(tid)d"
% locals())
self.volume.driver._execute("sudo", "ietadm", "--op", "show",
"--tid=%(tid)d" % locals())
self.stream.truncate(0)
self.mox.ReplayAll()
@@ -355,8 +355,9 @@ class ISCSITestCase(DriverTestCase):
# the first vblade process isn't running
tid = db.volume_get_iscsi_target_num(self.context, volume_id_list[0])
self.mox.StubOutWithMock(self.volume.driver, '_execute')
self.volume.driver._execute("sudo ietadm --op show --tid=%(tid)d"
% locals()).AndRaise(exception.ProcessExecutionError())
self.volume.driver._execute("sudo", "ietadm", "--op", "show",
"--tid=%(tid)d" % locals()
).AndRaise(exception.ProcessExecutionError())
self.mox.ReplayAll()
self.assertRaises(exception.ProcessExecutionError,

View File

@@ -385,6 +385,14 @@ class XenAPIVMTestCase(test.TestCase):
#consistent with bridge specified in nova db
self.network = network_bk
def test_spawn_with_network_qos(self):
self._create_instance()
for vif_ref in xenapi_fake.get_all('VIF'):
vif_rec = xenapi_fake.get_record('VIF', vif_ref)
self.assertEquals(vif_rec['qos_algorithm_type'], 'ratelimit')
self.assertEquals(vif_rec['qos_algorithm_params']['kbps'],
str(4 * 1024))
def tearDown(self):
super(XenAPIVMTestCase, self).tearDown()
self.manager.delete_project(self.project)

View File

@@ -133,13 +133,14 @@ def fetchfile(url, target):
def execute(*cmd, **kwargs):
process_input = kwargs.get('process_input', None)
addl_env = kwargs.get('addl_env', None)
check_exit_code = kwargs.get('check_exit_code', 0)
stdin = kwargs.get('stdin', subprocess.PIPE)
stdout = kwargs.get('stdout', subprocess.PIPE)
stderr = kwargs.get('stderr', subprocess.PIPE)
attempts = kwargs.get('attempts', 1)
process_input = kwargs.pop('process_input', None)
addl_env = kwargs.pop('addl_env', None)
check_exit_code = kwargs.pop('check_exit_code', 0)
delay_on_retry = kwargs.pop('delay_on_retry', True)
attempts = kwargs.pop('attempts', 1)
if len(kwargs):
raise exception.Error(_('Got unknown keyword args '
'to utils.execute: %r') % kwargs)
cmd = map(str, cmd)
while attempts > 0:
@@ -149,8 +150,11 @@ def execute(*cmd, **kwargs):
env = os.environ.copy()
if addl_env:
env.update(addl_env)
obj = subprocess.Popen(cmd, stdin=stdin,
stdout=stdout, stderr=stderr, env=env)
obj = subprocess.Popen(cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env=env)
result = None
if process_input != None:
result = obj.communicate(process_input)
@@ -176,6 +180,7 @@ def execute(*cmd, **kwargs):
raise
else:
LOG.debug(_("%r failed. Retrying."), cmd)
if delay_on_retry:
greenthread.sleep(random.randint(20, 200) / 100.0)
@@ -262,13 +267,25 @@ def generate_mac():
return ':'.join(map(lambda x: "%02x" % x, mac))
def generate_password(length=20):
"""Generate a random sequence of letters and digits
to be used as a password. Note that this is not intended
to represent the ultimate in security.
# Default symbols to use for passwords. Avoids visually confusing characters.
# ~6 bits per symbol
DEFAULT_PASSWORD_SYMBOLS = ("23456789" # Removed: 0,1
"ABCDEFGHJKLMNPQRSTUVWXYZ" # Removed: I, O
"abcdefghijkmnopqrstuvwxyz") # Removed: l
# ~5 bits per symbol
EASIER_PASSWORD_SYMBOLS = ("23456789" # Removed: 0, 1
"ABCDEFGHJKLMNPQRSTUVWXYZ") # Removed: I, O
def generate_password(length=20, symbols=DEFAULT_PASSWORD_SYMBOLS):
"""Generate a random password from the supplied symbols.
Believed to be reasonably secure (with a reasonable password length!)
"""
chrs = string.letters + string.digits
return "".join([random.choice(chrs) for i in xrange(length)])
r = random.SystemRandom()
return "".join([r.choice(symbols) for _i in xrange(length)])
def last_octet(address):
@@ -518,6 +535,9 @@ def synchronized(name):
def wrap(f):
@functools.wraps(f)
def inner(*args, **kwargs):
LOG.debug(_("Attempting to grab %(lock)s for method "
"%(method)s..." % {"lock": name,
"method": f.__name__}))
lock = lockfile.FileLock(os.path.join(FLAGS.lock_path,
'nova-%s.lock' % name))
with lock:
@@ -526,18 +546,6 @@ def synchronized(name):
return wrap
def ensure_b64_encoding(val):
"""Safety method to ensure that values expected to be base64-encoded
actually are. If they are, the value is returned unchanged. Otherwise,
the encoded value is returned.
"""
try:
dummy = base64.decode(val)
return val
except TypeError:
return base64.b64encode(val)
def get_from_path(items, path):
""" Returns a list of items matching the specified path. Takes an
XPath-like expression e.g. prop1/prop2/prop3, and for each item in items,