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>
|
||||
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>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user