summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorChris Behrens <cbehrens@codestud.com>2011-05-26 19:30:23 +0000
committerChris Behrens <cbehrens@codestud.com>2011-05-26 19:30:23 +0000
commit4d8995813d070530f1b6aa4a8d07758cd7d74ec2 (patch)
tree7a9ffe96e68b07459c08c2753cf54361d04a0a1d /nova
parent899642030dd60541153ccee810d082816f92dd49 (diff)
parent146e3a7a4f38e17dfbdac12860cffa057c936ed4 (diff)
downloadnova-4d8995813d070530f1b6aa4a8d07758cd7d74ec2.tar.gz
nova-4d8995813d070530f1b6aa4a8d07758cd7d74ec2.tar.xz
nova-4d8995813d070530f1b6aa4a8d07758cd7d74ec2.zip
Merged trunk
Diffstat (limited to 'nova')
-rw-r--r--nova/api/openstack/limits.py4
-rw-r--r--nova/api/openstack/servers.py6
-rw-r--r--nova/api/openstack/views/limits.py36
-rw-r--r--nova/compute/api.py38
-rw-r--r--nova/compute/manager.py21
-rw-r--r--nova/db/api.py2
-rw-r--r--nova/db/sqlalchemy/api.py9
-rw-r--r--nova/network/vmwareapi_net.py4
-rw-r--r--nova/quota.py110
-rw-r--r--nova/tests/api/openstack/test_limits.py138
-rw-r--r--nova/tests/api/openstack/test_servers.py3
-rw-r--r--nova/tests/test_quota.py80
-rw-r--r--nova/virt/xenapi/vmops.py11
13 files changed, 362 insertions, 100 deletions
diff --git a/nova/api/openstack/limits.py b/nova/api/openstack/limits.py
index 47bc238f1..bd0250a7f 100644
--- a/nova/api/openstack/limits.py
+++ b/nova/api/openstack/limits.py
@@ -30,6 +30,7 @@ from collections import defaultdict
from webob.dec import wsgify
+from nova import quota
from nova import wsgi
from nova.api.openstack import common
from nova.api.openstack import faults
@@ -64,7 +65,8 @@ class LimitsController(common.OpenstackController):
"""
Return all global and rate limit information.
"""
- abs_limits = {}
+ context = req.environ['nova.context']
+ abs_limits = quota.get_project_quotas(context, context.project_id)
rate_limits = req.environ.get("nova.limits", [])
builder = self._get_view_builder(req)
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index fcb630fae..5c10fc916 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -180,7 +180,8 @@ class Controller(common.OpenstackController):
key_name=key_name,
key_data=key_data,
metadata=env['server'].get('metadata', {}),
- injected_files=injected_files)
+ injected_files=injected_files,
+ admin_password=password)
except quota.QuotaError as error:
self._handle_quota_error(error)
@@ -190,8 +191,6 @@ class Controller(common.OpenstackController):
builder = self._get_view_builder(req)
server = builder.build(inst, is_detail=True)
server['server']['adminPass'] = password
- self.compute_api.set_admin_password(context, server['server']['id'],
- password)
return server
def _deserialize_create(self, request):
@@ -608,7 +607,6 @@ class ControllerV10(Controller):
def _parse_update(self, context, server_id, inst_dict, update_dict):
if 'adminPass' in inst_dict['server']:
- update_dict['admin_pass'] = inst_dict['server']['adminPass']
self.compute_api.set_admin_password(context, server_id,
inst_dict['server']['adminPass'])
diff --git a/nova/api/openstack/views/limits.py b/nova/api/openstack/views/limits.py
index 22d1c260d..e21c9f2fd 100644
--- a/nova/api/openstack/views/limits.py
+++ b/nova/api/openstack/views/limits.py
@@ -45,6 +45,34 @@ class ViewBuilder(object):
return output
+ def _build_absolute_limits(self, absolute_limits):
+ """Builder for absolute limits
+
+ absolute_limits should be given as a dict of limits.
+ For example: {"ram": 512, "gigabytes": 1024}.
+
+ """
+ limit_names = {
+ "ram": ["maxTotalRAMSize"],
+ "instances": ["maxTotalInstances"],
+ "cores": ["maxTotalCores"],
+ "metadata_items": ["maxServerMeta", "maxImageMeta"],
+ "injected_files": ["maxPersonality"],
+ "injected_file_content_bytes": ["maxPersonalitySize"],
+ }
+ limits = {}
+ for name, value in absolute_limits.iteritems():
+ if name in limit_names and value is not None:
+ for name in limit_names[name]:
+ limits[name] = value
+ return limits
+
+ def _build_rate_limits(self, rate_limits):
+ raise NotImplementedError()
+
+ def _build_rate_limit(self, rate_limit):
+ raise NotImplementedError()
+
class ViewBuilderV10(ViewBuilder):
"""Openstack API v1.0 limits view builder."""
@@ -63,9 +91,6 @@ class ViewBuilderV10(ViewBuilder):
"resetTime": rate_limit["resetTime"],
}
- def _build_absolute_limits(self, absolute_limit):
- return {}
-
class ViewBuilderV11(ViewBuilder):
"""Openstack API v1.1 limits view builder."""
@@ -79,7 +104,7 @@ class ViewBuilderV11(ViewBuilder):
# check for existing key
for limit in limits:
if limit["uri"] == rate_limit["URI"] and \
- limit["regex"] == limit["regex"]:
+ limit["regex"] == rate_limit["regex"]:
_rate_limit_key = limit
break
@@ -104,6 +129,3 @@ class ViewBuilderV11(ViewBuilder):
"unit": rate_limit["unit"],
"next-available": rate_limit["resetTime"],
}
-
- def _build_absolute_limits(self, absolute_limit):
- return {}
diff --git a/nova/compute/api.py b/nova/compute/api.py
index a12b7dee5..4f2363387 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -95,14 +95,15 @@ class API(base.Base):
"""
if injected_files is None:
return
- limit = quota.allowed_injected_files(context)
+ limit = quota.allowed_injected_files(context, len(injected_files))
if len(injected_files) > limit:
raise quota.QuotaError(code="OnsetFileLimitExceeded")
path_limit = quota.allowed_injected_file_path_bytes(context)
- content_limit = quota.allowed_injected_file_content_bytes(context)
for path, content in injected_files:
if len(path) > path_limit:
raise quota.QuotaError(code="OnsetFilePathLimitExceeded")
+ content_limit = quota.allowed_injected_file_content_bytes(
+ context, len(content))
if len(content) > content_limit:
raise quota.QuotaError(code="OnsetFileContentLimitExceeded")
@@ -134,7 +135,8 @@ class API(base.Base):
display_name='', display_description='',
key_name=None, key_data=None, security_group='default',
availability_zone=None, user_data=None, metadata={},
- injected_files=None):
+ injected_files=None,
+ admin_password=None):
"""Create the number and type of instances requested.
Verifies that quota and other arguments are valid.
@@ -149,9 +151,13 @@ class API(base.Base):
pid = context.project_id
LOG.warn(_("Quota exceeeded for %(pid)s,"
" tried to run %(min_count)s instances") % locals())
- raise quota.QuotaError(_("Instance quota exceeded. You can only "
- "run %s more instances of this type.") %
- num_instances, "InstanceLimitExceeded")
+ if num_instances <= 0:
+ message = _("Instance quota exceeded. You cannot run any "
+ "more instances of this type.")
+ else:
+ message = _("Instance quota exceeded. You can only run %s "
+ "more instances of this type.") % num_instances
+ raise quota.QuotaError(message, "InstanceLimitExceeded")
self._check_metadata_properties_quota(context, metadata)
self._check_injected_file_quota(context, injected_files)
@@ -264,7 +270,8 @@ class API(base.Base):
"instance_id": instance_id,
"instance_type": instance_type,
"availability_zone": availability_zone,
- "injected_files": injected_files}})
+ "injected_files": injected_files,
+ "admin_password": admin_password}})
for group_id in security_groups:
self.trigger_security_group_members_refresh(elevated, group_id)
@@ -503,15 +510,6 @@ class API(base.Base):
raise exception.Error(_("Unable to find host for Instance %s")
% instance_id)
- def _set_admin_password(self, context, instance_id, password):
- """Set the root/admin password for the given instance."""
- host = self._find_host(context, instance_id)
-
- rpc.cast(context,
- self.db.queue_get_for(context, FLAGS.compute_topic, host),
- {"method": "set_admin_password",
- "args": {"instance_id": instance_id, "new_pass": password}})
-
def snapshot(self, context, instance_id, name):
"""Snapshot the given instance.
@@ -665,8 +663,12 @@ class API(base.Base):
def set_admin_password(self, context, instance_id, password=None):
"""Set the root/admin password for the given instance."""
- eventlet.spawn_n(self._set_admin_password, context, instance_id,
- password)
+ host = self._find_host(context, instance_id)
+
+ rpc.cast(context,
+ self.db.queue_get_for(context, FLAGS.compute_topic, host),
+ {"method": "set_admin_password",
+ "args": {"instance_id": instance_id, "new_pass": password}})
def inject_file(self, context, instance_id):
"""Write a file to the given instance."""
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 11565c25e..d1e01f275 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -221,6 +221,7 @@ class ComputeManager(manager.SchedulerDependentManager):
context = context.elevated()
instance_ref = self.db.instance_get(context, instance_id)
instance_ref.injected_files = kwargs.get('injected_files', [])
+ instance_ref.admin_pass = kwargs.get('admin_password', None)
if instance_ref['name'] in self.driver.list_instances():
raise exception.Error(_("Instance has already been created"))
LOG.audit(_("instance %s: starting..."), instance_id,
@@ -405,22 +406,28 @@ class ComputeManager(manager.SchedulerDependentManager):
@exception.wrap_exception
@checks_instance_lock
def set_admin_password(self, context, instance_id, new_pass=None):
- """Set the root/admin password for an instance on this host."""
+ """Set the root/admin password for an instance on this host.
+
+ This is generally only called by API password resets after an
+ image has been built.
+ """
+
context = context.elevated()
if new_pass is None:
# Generate a random password
new_pass = utils.generate_password(FLAGS.password_length)
- while True:
+ max_tries = 10
+
+ for i in xrange(max_tries):
instance_ref = self.db.instance_get(context, instance_id)
instance_id = instance_ref["id"]
instance_state = instance_ref["state"]
expected_state = power_state.RUNNING
if instance_state != expected_state:
- time.sleep(5)
- continue
+ raise exception.Error(_('Instance is not running'))
else:
try:
self.driver.set_admin_password(instance_ref, new_pass)
@@ -436,6 +443,12 @@ class ComputeManager(manager.SchedulerDependentManager):
except Exception, e:
# Catch all here because this could be anything.
LOG.exception(e)
+ if i == max_tries - 1:
+ # At some point this exception may make it back
+ # to the API caller, and we don't want to reveal
+ # too much. The real exception is logged above
+ raise exception.Error(_('Internal error'))
+ time.sleep(1)
continue
@exception.wrap_exception
diff --git a/nova/db/api.py b/nova/db/api.py
index ef8aa1143..310c0bb09 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -403,7 +403,7 @@ def instance_create(context, values):
def instance_data_get_for_project(context, project_id):
- """Get (instance_count, core_count) for project."""
+ """Get (instance_count, total_cores, total_ram) for project."""
return IMPL.instance_data_get_for_project(context, project_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index b53e81053..e4dda5c12 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -803,12 +803,13 @@ def instance_create(context, values):
def instance_data_get_for_project(context, project_id):
session = get_session()
result = session.query(func.count(models.Instance.id),
- func.sum(models.Instance.vcpus)).\
+ func.sum(models.Instance.vcpus),
+ func.sum(models.Instance.memory_mb)).\
filter_by(project_id=project_id).\
filter_by(deleted=False).\
first()
# NOTE(vish): convert None to 0
- return (result[0] or 0, result[1] or 0)
+ return (result[0] or 0, result[1] or 0, result[2] or 0)
@require_context
@@ -1499,7 +1500,7 @@ def auth_token_create(_context, token):
###################
-@require_admin_context
+@require_context
def quota_get(context, project_id, resource, session=None):
if not session:
session = get_session()
@@ -1513,7 +1514,7 @@ def quota_get(context, project_id, resource, session=None):
return result
-@require_admin_context
+@require_context
def quota_get_all_by_project(context, project_id):
session = get_session()
result = {'project_id': project_id}
diff --git a/nova/network/vmwareapi_net.py b/nova/network/vmwareapi_net.py
index 373060add..04210c011 100644
--- a/nova/network/vmwareapi_net.py
+++ b/nova/network/vmwareapi_net.py
@@ -30,9 +30,7 @@ LOG = logging.getLogger("nova.network.vmwareapi_net")
FLAGS = flags.FLAGS
-flags.DEFINE_string('vlan_interface', 'vmnic0',
- 'Physical network adapter name in VMware ESX host for '
- 'vlan networking')
+FLAGS['vlan_interface'].SetDefault('vmnic0')
def ensure_vlan_bridge(vlan_num, bridge, net_attrs=None):
diff --git a/nova/quota.py b/nova/quota.py
index a93cd0766..58766e846 100644
--- a/nova/quota.py
+++ b/nova/quota.py
@@ -28,6 +28,8 @@ flags.DEFINE_integer('quota_instances', 10,
'number of instances allowed per project')
flags.DEFINE_integer('quota_cores', 20,
'number of instance cores allowed per project')
+flags.DEFINE_integer('quota_ram', 50 * 1024,
+ 'megabytes of instance ram allowed per project')
flags.DEFINE_integer('quota_volumes', 10,
'number of volumes allowed per project')
flags.DEFINE_integer('quota_gigabytes', 1000,
@@ -44,14 +46,28 @@ flags.DEFINE_integer('quota_max_injected_file_path_bytes', 255,
'number of bytes allowed per injected file path')
-def get_quota(context, project_id):
- rval = {'instances': FLAGS.quota_instances,
- 'cores': FLAGS.quota_cores,
- 'volumes': FLAGS.quota_volumes,
- 'gigabytes': FLAGS.quota_gigabytes,
- 'floating_ips': FLAGS.quota_floating_ips,
- 'metadata_items': FLAGS.quota_metadata_items}
-
+def _get_default_quotas():
+ defaults = {
+ 'instances': FLAGS.quota_instances,
+ 'cores': FLAGS.quota_cores,
+ 'ram': FLAGS.quota_ram,
+ 'volumes': FLAGS.quota_volumes,
+ 'gigabytes': FLAGS.quota_gigabytes,
+ 'floating_ips': FLAGS.quota_floating_ips,
+ 'metadata_items': FLAGS.quota_metadata_items,
+ 'injected_files': FLAGS.quota_max_injected_files,
+ 'injected_file_content_bytes':
+ FLAGS.quota_max_injected_file_content_bytes,
+ }
+ # -1 in the quota flags means unlimited
+ for key in defaults.keys():
+ if defaults[key] == -1:
+ defaults[key] = None
+ return defaults
+
+
+def get_project_quotas(context, project_id):
+ rval = _get_default_quotas()
quota = db.quota_get_all_by_project(context, project_id)
for key in rval.keys():
if key in quota:
@@ -65,71 +81,81 @@ def _get_request_allotment(requested, used, quota):
return quota - used
-def allowed_instances(context, num_instances, instance_type):
- """Check quota and return min(num_instances, allowed_instances)."""
+def allowed_instances(context, requested_instances, instance_type):
+ """Check quota and return min(requested_instances, allowed_instances)."""
project_id = context.project_id
context = context.elevated()
- num_cores = num_instances * instance_type['vcpus']
- used_instances, used_cores = db.instance_data_get_for_project(context,
- project_id)
- quota = get_quota(context, project_id)
- allowed_instances = _get_request_allotment(num_instances, used_instances,
+ requested_cores = requested_instances * instance_type['vcpus']
+ requested_ram = requested_instances * instance_type['memory_mb']
+ usage = db.instance_data_get_for_project(context, project_id)
+ used_instances, used_cores, used_ram = usage
+ quota = get_project_quotas(context, project_id)
+ allowed_instances = _get_request_allotment(requested_instances,
+ used_instances,
quota['instances'])
- allowed_cores = _get_request_allotment(num_cores, used_cores,
+ allowed_cores = _get_request_allotment(requested_cores, used_cores,
quota['cores'])
+ allowed_ram = _get_request_allotment(requested_ram, used_ram, quota['ram'])
allowed_instances = min(allowed_instances,
- int(allowed_cores // instance_type['vcpus']))
- return min(num_instances, allowed_instances)
+ allowed_cores // instance_type['vcpus'],
+ allowed_ram // instance_type['memory_mb'])
+ return min(requested_instances, allowed_instances)
-def allowed_volumes(context, num_volumes, size):
- """Check quota and return min(num_volumes, allowed_volumes)."""
+def allowed_volumes(context, requested_volumes, size):
+ """Check quota and return min(requested_volumes, allowed_volumes)."""
project_id = context.project_id
context = context.elevated()
size = int(size)
- num_gigabytes = num_volumes * size
+ requested_gigabytes = requested_volumes * size
used_volumes, used_gigabytes = db.volume_data_get_for_project(context,
project_id)
- quota = get_quota(context, project_id)
- allowed_volumes = _get_request_allotment(num_volumes, used_volumes,
+ quota = get_project_quotas(context, project_id)
+ allowed_volumes = _get_request_allotment(requested_volumes, used_volumes,
quota['volumes'])
- allowed_gigabytes = _get_request_allotment(num_gigabytes, used_gigabytes,
+ allowed_gigabytes = _get_request_allotment(requested_gigabytes,
+ used_gigabytes,
quota['gigabytes'])
allowed_volumes = min(allowed_volumes,
int(allowed_gigabytes // size))
- return min(num_volumes, allowed_volumes)
+ return min(requested_volumes, allowed_volumes)
-def allowed_floating_ips(context, num_floating_ips):
- """Check quota and return min(num_floating_ips, allowed_floating_ips)."""
+def allowed_floating_ips(context, requested_floating_ips):
+ """Check quota and return min(requested, allowed) floating ips."""
project_id = context.project_id
context = context.elevated()
used_floating_ips = db.floating_ip_count_by_project(context, project_id)
- quota = get_quota(context, project_id)
- allowed_floating_ips = _get_request_allotment(num_floating_ips,
+ quota = get_project_quotas(context, project_id)
+ allowed_floating_ips = _get_request_allotment(requested_floating_ips,
used_floating_ips,
quota['floating_ips'])
- return min(num_floating_ips, allowed_floating_ips)
+ return min(requested_floating_ips, allowed_floating_ips)
-def allowed_metadata_items(context, num_metadata_items):
- """Check quota; return min(num_metadata_items,allowed_metadata_items)."""
- project_id = context.project_id
- context = context.elevated()
- quota = get_quota(context, project_id)
- allowed_metadata_items = _get_request_allotment(num_metadata_items, 0,
- quota['metadata_items'])
- return min(num_metadata_items, allowed_metadata_items)
+def _calculate_simple_quota(context, resource, requested):
+ """Check quota for resource; return min(requested, allowed)."""
+ quota = get_project_quotas(context, context.project_id)
+ allowed = _get_request_allotment(requested, 0, quota[resource])
+ return min(requested, allowed)
+
+
+def allowed_metadata_items(context, requested_metadata_items):
+ """Return the number of metadata items allowed."""
+ return _calculate_simple_quota(context, 'metadata_items',
+ requested_metadata_items)
-def allowed_injected_files(context):
+def allowed_injected_files(context, requested_injected_files):
"""Return the number of injected files allowed."""
- return FLAGS.quota_max_injected_files
+ return _calculate_simple_quota(context, 'injected_files',
+ requested_injected_files)
-def allowed_injected_file_content_bytes(context):
+def allowed_injected_file_content_bytes(context, requested_bytes):
"""Return the number of bytes allowed per injected file content."""
- return FLAGS.quota_max_injected_file_content_bytes
+ resource = 'injected_file_content_bytes'
+ return _calculate_simple_quota(context, resource, requested_bytes)
def allowed_injected_file_path_bytes(context):
diff --git a/nova/tests/api/openstack/test_limits.py b/nova/tests/api/openstack/test_limits.py
index 45bd4d501..70f59eda6 100644
--- a/nova/tests/api/openstack/test_limits.py
+++ b/nova/tests/api/openstack/test_limits.py
@@ -27,6 +27,7 @@ import webob
from xml.dom.minidom import parseString
+import nova.context
from nova.api.openstack import limits
@@ -47,6 +48,13 @@ class BaseLimitTestSuite(unittest.TestCase):
self.time = 0.0
self.stubs = stubout.StubOutForTesting()
self.stubs.Set(limits.Limit, "_get_time", self._get_time)
+ self.absolute_limits = {}
+
+ def stub_get_project_quotas(context, project_id):
+ return self.absolute_limits
+
+ self.stubs.Set(nova.quota, "get_project_quotas",
+ stub_get_project_quotas)
def tearDown(self):
"""Run after each test."""
@@ -75,6 +83,8 @@ class LimitsControllerV10Test(BaseLimitTestSuite):
"action": "index",
"controller": "",
})
+ context = nova.context.RequestContext('testuser', 'testproject')
+ request.environ["nova.context"] = context
return request
def _populate_limits(self, request):
@@ -86,6 +96,18 @@ class LimitsControllerV10Test(BaseLimitTestSuite):
request.environ["nova.limits"] = _limits
return request
+ def _setup_absolute_limits(self):
+ self.absolute_limits = {
+ 'instances': 5,
+ 'cores': 8,
+ 'ram': 2 ** 13,
+ 'volumes': 21,
+ 'gigabytes': 34,
+ 'metadata_items': 55,
+ 'injected_files': 89,
+ 'injected_file_content_bytes': 144,
+ }
+
def test_empty_index_json(self):
"""Test getting empty limit details in JSON."""
request = self._get_index_request()
@@ -103,6 +125,7 @@ class LimitsControllerV10Test(BaseLimitTestSuite):
"""Test getting limit details in JSON."""
request = self._get_index_request()
request = self._populate_limits(request)
+ self._setup_absolute_limits()
response = request.get_response(self.controller)
expected = {
"limits": {
@@ -124,7 +147,15 @@ class LimitsControllerV10Test(BaseLimitTestSuite):
"remaining": 5,
"unit": "HOUR",
}],
- "absolute": {},
+ "absolute": {
+ "maxTotalInstances": 5,
+ "maxTotalCores": 8,
+ "maxTotalRAMSize": 2 ** 13,
+ "maxServerMeta": 55,
+ "maxImageMeta": 55,
+ "maxPersonality": 89,
+ "maxPersonalitySize": 144,
+ },
},
}
body = json.loads(response.body)
@@ -188,6 +219,8 @@ class LimitsControllerV11Test(BaseLimitTestSuite):
"action": "index",
"controller": "",
})
+ context = nova.context.RequestContext('testuser', 'testproject')
+ request.environ["nova.context"] = context
return request
def _populate_limits(self, request):
@@ -218,6 +251,11 @@ class LimitsControllerV11Test(BaseLimitTestSuite):
"""Test getting limit details in JSON."""
request = self._get_index_request()
request = self._populate_limits(request)
+ self.absolute_limits = {
+ 'ram': 512,
+ 'instances': 5,
+ 'cores': 21,
+ }
response = request.get_response(self.controller)
expected = {
"limits": {
@@ -257,12 +295,110 @@ class LimitsControllerV11Test(BaseLimitTestSuite):
},
],
+ "absolute": {
+ "maxTotalRAMSize": 512,
+ "maxTotalInstances": 5,
+ "maxTotalCores": 21,
+ },
+ },
+ }
+ body = json.loads(response.body)
+ self.assertEqual(expected, body)
+
+ def _populate_limits_diff_regex(self, request):
+ """Put limit info into a request."""
+ _limits = [
+ limits.Limit("GET", "*", ".*", 10, 60).display(),
+ limits.Limit("GET", "*", "*.*", 10, 60).display(),
+ ]
+ request.environ["nova.limits"] = _limits
+ return request
+
+ def test_index_diff_regex(self):
+ """Test getting limit details in JSON."""
+ request = self._get_index_request()
+ request = self._populate_limits_diff_regex(request)
+ response = request.get_response(self.controller)
+ expected = {
+ "limits": {
+ "rate": [
+ {
+ "regex": ".*",
+ "uri": "*",
+ "limit": [
+ {
+ "verb": "GET",
+ "next-available": 0,
+ "unit": "MINUTE",
+ "value": 10,
+ "remaining": 10,
+ },
+ ],
+ },
+ {
+ "regex": "*.*",
+ "uri": "*",
+ "limit": [
+ {
+ "verb": "GET",
+ "next-available": 0,
+ "unit": "MINUTE",
+ "value": 10,
+ "remaining": 10,
+ },
+ ],
+ },
+
+ ],
"absolute": {},
},
}
body = json.loads(response.body)
self.assertEqual(expected, body)
+ def _test_index_absolute_limits_json(self, expected):
+ request = self._get_index_request()
+ response = request.get_response(self.controller)
+ body = json.loads(response.body)
+ self.assertEqual(expected, body['limits']['absolute'])
+
+ def test_index_ignores_extra_absolute_limits_json(self):
+ self.absolute_limits = {'unknown_limit': 9001}
+ self._test_index_absolute_limits_json({})
+
+ def test_index_absolute_ram_json(self):
+ self.absolute_limits = {'ram': 1024}
+ self._test_index_absolute_limits_json({'maxTotalRAMSize': 1024})
+
+ def test_index_absolute_cores_json(self):
+ self.absolute_limits = {'cores': 17}
+ self._test_index_absolute_limits_json({'maxTotalCores': 17})
+
+ def test_index_absolute_instances_json(self):
+ self.absolute_limits = {'instances': 19}
+ self._test_index_absolute_limits_json({'maxTotalInstances': 19})
+
+ def test_index_absolute_metadata_json(self):
+ # NOTE: both server metadata and image metadata are overloaded
+ # into metadata_items
+ self.absolute_limits = {'metadata_items': 23}
+ expected = {
+ 'maxServerMeta': 23,
+ 'maxImageMeta': 23,
+ }
+ self._test_index_absolute_limits_json(expected)
+
+ def test_index_absolute_injected_files(self):
+ self.absolute_limits = {
+ 'injected_files': 17,
+ 'injected_file_content_bytes': 86753,
+ }
+ expected = {
+ 'maxPersonality': 17,
+ 'maxPersonalitySize': 86753,
+ }
+ self._test_index_absolute_limits_json(expected)
+
class LimitMiddlewareTest(BaseLimitTestSuite):
"""
diff --git a/nova/tests/api/openstack/test_servers.py b/nova/tests/api/openstack/test_servers.py
index dc8815845..fbde5c9ce 100644
--- a/nova/tests/api/openstack/test_servers.py
+++ b/nova/tests/api/openstack/test_servers.py
@@ -774,8 +774,7 @@ class ServersTest(test.TestCase):
def server_update(context, id, params):
filtered_dict = dict(
- display_name='server_test',
- admin_pass='bacon',
+ display_name='server_test'
)
self.assertEqual(params, filtered_dict)
return filtered_dict
diff --git a/nova/tests/test_quota.py b/nova/tests/test_quota.py
index 7ace2ad7d..916fca55e 100644
--- a/nova/tests/test_quota.py
+++ b/nova/tests/test_quota.py
@@ -104,6 +104,10 @@ class QuotaTestCase(test.TestCase):
num_instances = quota.allowed_instances(self.context, 100,
self._get_instance_type('m1.small'))
self.assertEqual(num_instances, 10)
+ db.quota_create(self.context, self.project.id, 'ram', 3 * 2048)
+ num_instances = quota.allowed_instances(self.context, 100,
+ self._get_instance_type('m1.small'))
+ self.assertEqual(num_instances, 3)
# metadata_items
too_many_items = FLAGS.quota_metadata_items + 1000
@@ -120,7 +124,8 @@ class QuotaTestCase(test.TestCase):
def test_unlimited_instances(self):
FLAGS.quota_instances = 2
- FLAGS.quota_cores = 1000
+ FLAGS.quota_ram = -1
+ FLAGS.quota_cores = -1
instance_type = self._get_instance_type('m1.small')
num_instances = quota.allowed_instances(self.context, 100,
instance_type)
@@ -133,8 +138,25 @@ class QuotaTestCase(test.TestCase):
instance_type)
self.assertEqual(num_instances, 101)
+ def test_unlimited_ram(self):
+ FLAGS.quota_instances = -1
+ FLAGS.quota_ram = 2 * 2048
+ FLAGS.quota_cores = -1
+ instance_type = self._get_instance_type('m1.small')
+ num_instances = quota.allowed_instances(self.context, 100,
+ instance_type)
+ self.assertEqual(num_instances, 2)
+ db.quota_create(self.context, self.project.id, 'ram', None)
+ num_instances = quota.allowed_instances(self.context, 100,
+ instance_type)
+ self.assertEqual(num_instances, 100)
+ num_instances = quota.allowed_instances(self.context, 101,
+ instance_type)
+ self.assertEqual(num_instances, 101)
+
def test_unlimited_cores(self):
- FLAGS.quota_instances = 1000
+ FLAGS.quota_instances = -1
+ FLAGS.quota_ram = -1
FLAGS.quota_cores = 2
instance_type = self._get_instance_type('m1.small')
num_instances = quota.allowed_instances(self.context, 100,
@@ -150,7 +172,7 @@ class QuotaTestCase(test.TestCase):
def test_unlimited_volumes(self):
FLAGS.quota_volumes = 10
- FLAGS.quota_gigabytes = 1000
+ FLAGS.quota_gigabytes = -1
volumes = quota.allowed_volumes(self.context, 100, 1)
self.assertEqual(volumes, 10)
db.quota_create(self.context, self.project.id, 'volumes', None)
@@ -160,7 +182,7 @@ class QuotaTestCase(test.TestCase):
self.assertEqual(volumes, 101)
def test_unlimited_gigabytes(self):
- FLAGS.quota_volumes = 1000
+ FLAGS.quota_volumes = -1
FLAGS.quota_gigabytes = 10
volumes = quota.allowed_volumes(self.context, 100, 1)
self.assertEqual(volumes, 10)
@@ -274,10 +296,47 @@ class QuotaTestCase(test.TestCase):
image_id='fake',
metadata=metadata)
- def test_allowed_injected_files(self):
- self.assertEqual(
- quota.allowed_injected_files(self.context),
- FLAGS.quota_max_injected_files)
+ def test_default_allowed_injected_files(self):
+ FLAGS.quota_max_injected_files = 55
+ self.assertEqual(quota.allowed_injected_files(self.context, 100), 55)
+
+ def test_overridden_allowed_injected_files(self):
+ FLAGS.quota_max_injected_files = 5
+ db.quota_create(self.context, self.project.id, 'injected_files', 77)
+ self.assertEqual(quota.allowed_injected_files(self.context, 100), 77)
+
+ def test_unlimited_default_allowed_injected_files(self):
+ FLAGS.quota_max_injected_files = -1
+ self.assertEqual(quota.allowed_injected_files(self.context, 100), 100)
+
+ def test_unlimited_db_allowed_injected_files(self):
+ FLAGS.quota_max_injected_files = 5
+ db.quota_create(self.context, self.project.id, 'injected_files', None)
+ self.assertEqual(quota.allowed_injected_files(self.context, 100), 100)
+
+ def test_default_allowed_injected_file_content_bytes(self):
+ FLAGS.quota_max_injected_file_content_bytes = 12345
+ limit = quota.allowed_injected_file_content_bytes(self.context, 23456)
+ self.assertEqual(limit, 12345)
+
+ def test_overridden_allowed_injected_file_content_bytes(self):
+ FLAGS.quota_max_injected_file_content_bytes = 12345
+ db.quota_create(self.context, self.project.id,
+ 'injected_file_content_bytes', 5678)
+ limit = quota.allowed_injected_file_content_bytes(self.context, 23456)
+ self.assertEqual(limit, 5678)
+
+ def test_unlimited_default_allowed_injected_file_content_bytes(self):
+ FLAGS.quota_max_injected_file_content_bytes = -1
+ limit = quota.allowed_injected_file_content_bytes(self.context, 23456)
+ self.assertEqual(limit, 23456)
+
+ def test_unlimited_db_allowed_injected_file_content_bytes(self):
+ FLAGS.quota_max_injected_file_content_bytes = 12345
+ db.quota_create(self.context, self.project.id,
+ 'injected_file_content_bytes', None)
+ limit = quota.allowed_injected_file_content_bytes(self.context, 23456)
+ self.assertEqual(limit, 23456)
def _create_with_injected_files(self, files):
api = compute.API(image_service=self.StubImageService())
@@ -304,11 +363,6 @@ class QuotaTestCase(test.TestCase):
self.assertRaises(quota.QuotaError,
self._create_with_injected_files, files)
- def test_allowed_injected_file_content_bytes(self):
- self.assertEqual(
- quota.allowed_injected_file_content_bytes(self.context),
- FLAGS.quota_max_injected_file_content_bytes)
-
def test_max_injected_file_content_bytes(self):
max = FLAGS.quota_max_injected_file_content_bytes
content = ''.join(['a' for i in xrange(max)])
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 02e140dcc..6d516ddbc 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -219,6 +219,13 @@ class VMOps(object):
for path, contents in instance.injected_files:
LOG.debug(_("Injecting file path: '%s'") % path)
self.inject_file(instance, path, contents)
+
+ def _set_admin_password():
+ admin_password = instance.admin_pass
+ if admin_password:
+ LOG.debug(_("Setting admin password"))
+ self.set_admin_password(instance, admin_password)
+
# NOTE(armando): Do we really need to do this in virt?
# NOTE(tr3buchet): not sure but wherever we do it, we need to call
# reset_network afterwards
@@ -231,6 +238,7 @@ class VMOps(object):
LOG.debug(_('Instance %s: booted'), instance_name)
timer.stop()
_inject_files()
+ _set_admin_password()
return True
except Exception, exc:
LOG.warn(exc)
@@ -475,6 +483,9 @@ class VMOps(object):
# Successful return code from password is '0'
if resp_dict['returncode'] != '0':
raise RuntimeError(resp_dict['message'])
+ db.instance_update(context.get_admin_context(),
+ instance['id'],
+ dict(admin_pass=new_pass))
return resp_dict['message']
def inject_file(self, instance, path, contents):