merge trunk
This commit is contained in:
1
Authors
1
Authors
@@ -40,6 +40,7 @@ Ken Pepple <ken.pepple@gmail.com>
|
|||||||
Kevin L. Mitchell <kevin.mitchell@rackspace.com>
|
Kevin L. Mitchell <kevin.mitchell@rackspace.com>
|
||||||
Koji Iida <iida.koji@lab.ntt.co.jp>
|
Koji Iida <iida.koji@lab.ntt.co.jp>
|
||||||
Lorin Hochstein <lorin@isi.edu>
|
Lorin Hochstein <lorin@isi.edu>
|
||||||
|
Mark Washenberger <mark.washenberger@rackspace.com>
|
||||||
Masanori Itoh <itoumsn@nttdata.co.jp>
|
Masanori Itoh <itoumsn@nttdata.co.jp>
|
||||||
Matt Dietz <matt.dietz@rackspace.com>
|
Matt Dietz <matt.dietz@rackspace.com>
|
||||||
Michael Gundlach <michael.gundlach@rackspace.com>
|
Michael Gundlach <michael.gundlach@rackspace.com>
|
||||||
|
|||||||
@@ -446,10 +446,15 @@ class FixedIpCommands(object):
|
|||||||
def list(self, host=None):
|
def list(self, host=None):
|
||||||
"""Lists all fixed ips (optionally by host) arguments: [host]"""
|
"""Lists all fixed ips (optionally by host) arguments: [host]"""
|
||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
if host == None:
|
|
||||||
fixed_ips = db.fixed_ip_get_all(ctxt)
|
try:
|
||||||
else:
|
if host == None:
|
||||||
fixed_ips = db.fixed_ip_get_all_by_host(ctxt, host)
|
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'),
|
print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (_('network'),
|
||||||
_('IP address'),
|
_('IP address'),
|
||||||
@@ -466,9 +471,9 @@ class FixedIpCommands(object):
|
|||||||
host = instance['host']
|
host = instance['host']
|
||||||
mac_address = instance['mac_address']
|
mac_address = instance['mac_address']
|
||||||
print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (
|
print "%-18s\t%-15s\t%-17s\t%-15s\t%s" % (
|
||||||
fixed_ip['network']['cidr'],
|
fixed_ip['network']['cidr'],
|
||||||
fixed_ip['address'],
|
fixed_ip['address'],
|
||||||
mac_address, hostname, host)
|
mac_address, hostname, host)
|
||||||
|
|
||||||
|
|
||||||
class FloatingIpCommands(object):
|
class FloatingIpCommands(object):
|
||||||
@@ -574,8 +579,10 @@ class VmCommands(object):
|
|||||||
ctxt = context.get_admin_context()
|
ctxt = context.get_admin_context()
|
||||||
instance_id = ec2utils.ec2_id_to_id(ec2_id)
|
instance_id = ec2utils.ec2_id_to_id(ec2_id)
|
||||||
|
|
||||||
if FLAGS.connection_type != 'libvirt':
|
if (FLAGS.connection_type != 'libvirt' or
|
||||||
msg = _('Only KVM is supported for now. Sorry!')
|
(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)
|
raise exception.Error(msg)
|
||||||
|
|
||||||
if (FLAGS.volume_driver != 'nova.volume.driver.AOEDriver' and \
|
if (FLAGS.volume_driver != 'nova.volume.driver.AOEDriver' and \
|
||||||
@@ -716,6 +723,49 @@ class DbCommands(object):
|
|||||||
print migration.db_version()
|
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):
|
class VolumeCommands(object):
|
||||||
"""Methods for dealing with a cloud in an odd state"""
|
"""Methods for dealing with a cloud in an odd state"""
|
||||||
|
|
||||||
@@ -1002,7 +1052,8 @@ CATEGORIES = [
|
|||||||
('volume', VolumeCommands),
|
('volume', VolumeCommands),
|
||||||
('instance_type', InstanceTypeCommands),
|
('instance_type', InstanceTypeCommands),
|
||||||
('image', ImageCommands),
|
('image', ImageCommands),
|
||||||
('flavor', InstanceTypeCommands)]
|
('flavor', InstanceTypeCommands),
|
||||||
|
('instance', InstanceCommands)]
|
||||||
|
|
||||||
|
|
||||||
def lazy_match(name, key_value_tuples):
|
def lazy_match(name, key_value_tuples):
|
||||||
|
|||||||
@@ -162,6 +162,8 @@ class DbDriver(object):
|
|||||||
values['description'] = description
|
values['description'] = description
|
||||||
|
|
||||||
db.project_update(context.get_admin_context(), project_id, values)
|
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):
|
def add_to_project(self, uid, project_id):
|
||||||
"""Add user to project"""
|
"""Add user to project"""
|
||||||
|
|||||||
@@ -275,6 +275,8 @@ class LdapDriver(object):
|
|||||||
attr.append((self.ldap.MOD_REPLACE, 'description', description))
|
attr.append((self.ldap.MOD_REPLACE, 'description', description))
|
||||||
dn = self.__project_to_dn(project_id)
|
dn = self.__project_to_dn(project_id)
|
||||||
self.conn.modify_s(dn, attr)
|
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
|
@sanitize
|
||||||
def add_to_project(self, uid, project_id):
|
def add_to_project(self, uid, project_id):
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class Error(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class ApiError(Error):
|
class ApiError(Error):
|
||||||
def __init__(self, message='Unknown', code='Unknown'):
|
def __init__(self, message='Unknown', code='ApiError'):
|
||||||
self.message = message
|
self.message = message
|
||||||
self.code = code
|
self.code = code
|
||||||
super(ApiError, self).__init__('%s: %s' % (code, message))
|
super(ApiError, self).__init__('%s: %s' % (code, message))
|
||||||
|
|||||||
@@ -37,6 +37,12 @@ flags.DEFINE_integer('quota_floating_ips', 10,
|
|||||||
'number of floating ips allowed per project')
|
'number of floating ips allowed per project')
|
||||||
flags.DEFINE_integer('quota_metadata_items', 128,
|
flags.DEFINE_integer('quota_metadata_items', 128,
|
||||||
'number of metadata items allowed per instance')
|
'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):
|
def get_quota(context, project_id):
|
||||||
@@ -46,6 +52,7 @@ def get_quota(context, project_id):
|
|||||||
'gigabytes': FLAGS.quota_gigabytes,
|
'gigabytes': FLAGS.quota_gigabytes,
|
||||||
'floating_ips': FLAGS.quota_floating_ips,
|
'floating_ips': FLAGS.quota_floating_ips,
|
||||||
'metadata_items': FLAGS.quota_metadata_items}
|
'metadata_items': FLAGS.quota_metadata_items}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
quota = db.quota_get(context, project_id)
|
quota = db.quota_get(context, project_id)
|
||||||
for key in rval.keys():
|
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)
|
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):
|
class QuotaError(exception.ApiError):
|
||||||
"""Quota Exceeeded"""
|
"""Quota Exceeeded"""
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ 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..."))
|
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))
|
||||||
@@ -352,7 +352,7 @@ 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..."))
|
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)
|
||||||
|
|||||||
@@ -299,6 +299,13 @@ class AuthManagerTestCase(object):
|
|||||||
self.assertEqual('test2', project.project_manager_id)
|
self.assertEqual('test2', project.project_manager_id)
|
||||||
self.assertEqual('new desc', project.description)
|
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):
|
def test_can_delete_project(self):
|
||||||
with user_generator(self.manager):
|
with user_generator(self.manager):
|
||||||
self.manager.create_project('testproj', 'test1')
|
self.manager.create_project('testproj', 'test1')
|
||||||
|
|||||||
@@ -336,8 +336,8 @@ class ISCSITestCase(DriverTestCase):
|
|||||||
self.mox.StubOutWithMock(self.volume.driver, '_execute')
|
self.mox.StubOutWithMock(self.volume.driver, '_execute')
|
||||||
for i in volume_id_list:
|
for i in volume_id_list:
|
||||||
tid = db.volume_get_iscsi_target_num(self.context, i)
|
tid = db.volume_get_iscsi_target_num(self.context, i)
|
||||||
self.volume.driver._execute("sudo ietadm --op show --tid=%(tid)d"
|
self.volume.driver._execute("sudo", "ietadm", "--op", "show",
|
||||||
% locals())
|
"--tid=%(tid)d" % locals())
|
||||||
|
|
||||||
self.stream.truncate(0)
|
self.stream.truncate(0)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
@@ -355,8 +355,9 @@ class ISCSITestCase(DriverTestCase):
|
|||||||
# the first vblade process isn't running
|
# the first vblade process isn't running
|
||||||
tid = db.volume_get_iscsi_target_num(self.context, volume_id_list[0])
|
tid = db.volume_get_iscsi_target_num(self.context, volume_id_list[0])
|
||||||
self.mox.StubOutWithMock(self.volume.driver, '_execute')
|
self.mox.StubOutWithMock(self.volume.driver, '_execute')
|
||||||
self.volume.driver._execute("sudo ietadm --op show --tid=%(tid)d"
|
self.volume.driver._execute("sudo", "ietadm", "--op", "show",
|
||||||
% locals()).AndRaise(exception.ProcessExecutionError())
|
"--tid=%(tid)d" % locals()
|
||||||
|
).AndRaise(exception.ProcessExecutionError())
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
self.assertRaises(exception.ProcessExecutionError,
|
self.assertRaises(exception.ProcessExecutionError,
|
||||||
|
|||||||
@@ -385,6 +385,14 @@ class XenAPIVMTestCase(test.TestCase):
|
|||||||
#consistent with bridge specified in nova db
|
#consistent with bridge specified in nova db
|
||||||
self.network = network_bk
|
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):
|
def tearDown(self):
|
||||||
super(XenAPIVMTestCase, self).tearDown()
|
super(XenAPIVMTestCase, self).tearDown()
|
||||||
self.manager.delete_project(self.project)
|
self.manager.delete_project(self.project)
|
||||||
|
|||||||
@@ -133,13 +133,14 @@ def fetchfile(url, target):
|
|||||||
|
|
||||||
|
|
||||||
def execute(*cmd, **kwargs):
|
def execute(*cmd, **kwargs):
|
||||||
process_input = kwargs.get('process_input', None)
|
process_input = kwargs.pop('process_input', None)
|
||||||
addl_env = kwargs.get('addl_env', None)
|
addl_env = kwargs.pop('addl_env', None)
|
||||||
check_exit_code = kwargs.get('check_exit_code', 0)
|
check_exit_code = kwargs.pop('check_exit_code', 0)
|
||||||
stdin = kwargs.get('stdin', subprocess.PIPE)
|
delay_on_retry = kwargs.pop('delay_on_retry', True)
|
||||||
stdout = kwargs.get('stdout', subprocess.PIPE)
|
attempts = kwargs.pop('attempts', 1)
|
||||||
stderr = kwargs.get('stderr', subprocess.PIPE)
|
if len(kwargs):
|
||||||
attempts = kwargs.get('attempts', 1)
|
raise exception.Error(_('Got unknown keyword args '
|
||||||
|
'to utils.execute: %r') % kwargs)
|
||||||
cmd = map(str, cmd)
|
cmd = map(str, cmd)
|
||||||
|
|
||||||
while attempts > 0:
|
while attempts > 0:
|
||||||
@@ -149,8 +150,11 @@ def execute(*cmd, **kwargs):
|
|||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
if addl_env:
|
if addl_env:
|
||||||
env.update(addl_env)
|
env.update(addl_env)
|
||||||
obj = subprocess.Popen(cmd, stdin=stdin,
|
obj = subprocess.Popen(cmd,
|
||||||
stdout=stdout, stderr=stderr, env=env)
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
env=env)
|
||||||
result = None
|
result = None
|
||||||
if process_input != None:
|
if process_input != None:
|
||||||
result = obj.communicate(process_input)
|
result = obj.communicate(process_input)
|
||||||
@@ -176,7 +180,8 @@ def execute(*cmd, **kwargs):
|
|||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
LOG.debug(_("%r failed. Retrying."), cmd)
|
LOG.debug(_("%r failed. Retrying."), cmd)
|
||||||
greenthread.sleep(random.randint(20, 200) / 100.0)
|
if delay_on_retry:
|
||||||
|
greenthread.sleep(random.randint(20, 200) / 100.0)
|
||||||
|
|
||||||
|
|
||||||
def ssh_execute(ssh, cmd, process_input=None,
|
def ssh_execute(ssh, cmd, process_input=None,
|
||||||
@@ -262,13 +267,25 @@ def generate_mac():
|
|||||||
return ':'.join(map(lambda x: "%02x" % x, mac))
|
return ':'.join(map(lambda x: "%02x" % x, mac))
|
||||||
|
|
||||||
|
|
||||||
def generate_password(length=20):
|
# Default symbols to use for passwords. Avoids visually confusing characters.
|
||||||
"""Generate a random sequence of letters and digits
|
# ~6 bits per symbol
|
||||||
to be used as a password. Note that this is not intended
|
DEFAULT_PASSWORD_SYMBOLS = ("23456789" # Removed: 0,1
|
||||||
to represent the ultimate in security.
|
"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
|
r = random.SystemRandom()
|
||||||
return "".join([random.choice(chrs) for i in xrange(length)])
|
return "".join([r.choice(symbols) for _i in xrange(length)])
|
||||||
|
|
||||||
|
|
||||||
def last_octet(address):
|
def last_octet(address):
|
||||||
@@ -518,6 +535,9 @@ def synchronized(name):
|
|||||||
def wrap(f):
|
def wrap(f):
|
||||||
@functools.wraps(f)
|
@functools.wraps(f)
|
||||||
def inner(*args, **kwargs):
|
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,
|
lock = lockfile.FileLock(os.path.join(FLAGS.lock_path,
|
||||||
'nova-%s.lock' % name))
|
'nova-%s.lock' % name))
|
||||||
with lock:
|
with lock:
|
||||||
@@ -526,18 +546,6 @@ def synchronized(name):
|
|||||||
return wrap
|
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):
|
def get_from_path(items, path):
|
||||||
""" Returns a list of items matching the specified path. Takes an
|
""" 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,
|
XPath-like expression e.g. prop1/prop2/prop3, and for each item in items,
|
||||||
|
|||||||
Reference in New Issue
Block a user